具有'语义'的Java泛型,这可能吗?

时间:2011-07-19 18:25:22

标签: java generics

首先,如果我选择“语义”这个词,我会使用错误的术语。

出于各种显而易见的原因,我非常喜欢Java中的泛型。当我处理各种各样奇怪的代码时,它对我有很大的帮助,而且我经常要回到旧的东西。今天我发现自己有一个经典的参数错位错误,我可能不会在前通用时代写过 - 它们让我有点懒。

我想知道是否有一种语言功能,无论是用Java还是用其他语言中的类似概念,它都具有泛型的类型安全性,并将其扩展到一种语义安全性。具体来说,我想帮助捕获因将正确的东西放在错误的位置而导致的错误。

一个简单的例子:

Map<String, String> myMap = new HashMap<String, String>();
String myKey = "key";
String myVal = "value";
myMap.put(myVal, myKey);

没有编译器可以捕获。我想,我可以子类String 制作包装器给我Key类型和Value类型。我可以命名我的变量以表明它们的用途(就像我在这个例子中所做的那样)。还有什么?

所以我的问题是:

  • 出于学术兴趣,这个概念叫什么,有哪些语言有这个功能?
  • 在没有任何此类语言功能的情况下,避免此类错误的最佳做法是什么?

由于

5 个答案:

答案 0 :(得分:6)

我总是使用两种不同类型的地图作为键和值,而不是相同的类型。我想有时候你想要使用与你的例子中相同的类型作为键和值。我无法想到任何具有此功能的语言。

实际上我之前使用过Clojure,对于地图,它使用了一个关键字来表示:key,然后值就是值。它使它更清晰,但现在我正在涉及功能编程,这不是你问的问题。查看类似Lisp的语言。

我认为这只是一个没有让他们混淆的情况,Java当然不能强制执行这种检查。

最好的建议是为键和值使用不同的类型,以便编译器可以检测类型错误,或者像您一样明确地命名键和值变量。

答案 1 :(得分:4)

你不能继承String。但是,您可以创建自己的类,并使用它们。因此,例如,如果要将人员映射到角色,则可以定义类似

的人员
class Person {
  String id;
  String firstName;
  String lastName;
   ...
}

这样的角色
class Role {
    int id;
    String name;
}

(注意这些将需要定义一个equals和hashcode方法)

并且具有从人到角色的映射:

Map<Person, Set<Role>> myMap = new HashMap<Person, Set<Role>>();

以便类型系统强制执行您可以放在哪里(而不是将字符串映射到整数组)。不确定这是什么,但“域对象”和“抽象数据类型”似乎相关。

在没有类型检查的情况下,后备是编写单元测试。

答案 2 :(得分:4)

您可以使用更广泛的类型系统,避免在参数列表中使用相同的类型。

即。

Map<Key, Value> myMap = new HashMap<Key, Value>();
Key myKey = // some key
Value myVal = // some value
myMap.put(myVal, myKey); //compiler error

在某种程度上,您需要解释概念以及它们如何与操作交互以使语言检查任何内容,并对是否有任何替代方法感兴趣。

答案 3 :(得分:2)

我不知道任何可以解决这个问题的语言概念。基本上你要求的是一个思维阅读编译器。即。

  1. 您希望能够制作成对地图。
  2. 您希望(大概)能够使用您选择的任何变量名称。
  3. 您希望编译器检测到您以不正确的顺序传递参数。
  4. 我唯一的想法是语言可以强加语法约束来模仿所需的语义约束。例如,它可能要求您命名要用作键“key_somename_”的变量,以及用于值的变量。有点改进,但​​它限制了程序员。

    如果您真的担心它,您可以在Java中扩展Map以仅接受实现虚拟“密钥”接口的类型,并生成每次需要密钥类时实现该接口的子类型。

答案 4 :(得分:2)

这很糟糕但是有效:

public static <K extends String,V extends String> void test() {
  K key = (K) "foo";
  V val = (V) "bar";
  Map<K,V> map = new HashMap<K,V>();
  map.put(key,val);
}

现在它更有趣了:

// The whole code is using type parameters like this:
public static <K extends String,V extends String> void test(K key, V value) {
  Map<K,V> map = new HashMap<K,V>();
  map.put(key,val);
}

// At the end, use the real type (K=String, V=String);
public static void main(String[) args) {
  test<String,String>("foo","bar");
};