【问题标题】:Method calling and wildcard方法调用和通配符
【发布时间】:2013-10-01 08:57:51
【问题描述】:

考虑以下代码:

MyClass myClass= new MyExtendedClass();
myClass.method();

其中 MyExtendedClass 是 MyClass 的子类型。正如我在第二个字符串编译器上的编译状态编译器上所理解的那样,在MyClass 的源代码中检查了存在方法method()。是正确的推理吗?现在考虑

List<Integer> ints= new ArrayList<Integer>();
ints.add(2);
List<? extends Integer> lst = ints;
lst.get(0);

在哪里可以看到List&lt;? extends Integer&gt; 的来源? 现在考虑:

new ArrayList<Integer>().getClass().equals(
    new ArrayList<String>().getClass());// return true.

所以 在运行时类ArrayList&lt;Integer&gt;ArrayList&lt;String&gt; 是相等的,但在编译状态它不是真的。为什么? ArrayList&lt;String&gt;ArrayList&lt;Integer&gt;的来源在哪里?

【问题讨论】:

标签: java generics arraylist wildcard


【解决方案1】:

Java 不像 C++ 那样工作。在 C++ 中,“模板”会导致完整生成具有该引用类型的新类。然而,Java 不会为每个模板化实例生成一个新类。相反,类型信息在编译和运行时作为设置携带。所以 ArrayList 和 ArrayList 使用相同的实际类。

类型检查在编译器中,对于简单的类来说这很容易。

class Gem {...}
class Ruby extends Gem {...}
class Diamond extends Gem {...}

Gem a;
Ruby b;
Diamond c;

a = b;   //no problem allowed
b = a;   //type problem, need to cast it!
b = c;   //never allowed!

但是参数化的类要复杂得多

List<Gem> la;
List<Ruby> lb;
List<Diamond> lc;

la = lb;   //not allowed because type is different even though Ruby extends Gem

List<? extends Gem> ld;

ld = la;  //allowed
ld = lb;  //also allowed
ld = lc;  //also allowed

需要通配符才能让您拥有一个包含多种集合的集合变量,其中不同之处在于类型参数。在所有情况下,对象类(和源代码)都保持不变。

【讨论】:

  • 那是我想像的。 ArrayList&lt;Integer&gt;ArrayList&lt;Integer&gt; 使用同一个类。
  • @AgilePro 我理解你,在运行时我们只有原始类型。但是编译器将ArrayList&lt;Integer&gt;ArrayList&lt;String&gt; 视为不同的类。因此,如果我们在List&lt;T&gt; 的源代码中调用List&lt;Integer&gt; lst = ints; lst.add(0); 编译器检查方法add(int),其中T 定义为Integer。这是正确的?但是如果他看到List&lt;? extends Integer&gt; lst = ints; lst.add(0);,编译器在做什么?
  • 单一类型类的继承是显而易见的——A 是 B 的超类,或者不是。但是参数化的类增加了额外的维度。通配符允许您在编译器所做的检查方面具有更大的灵活性。这对 Integer 和 String 没有任何作用,因为它们与任何父类无关。但是,如果您确实想要拥有相关类的集合,通配符允许您更轻松地传递它们。阅读docs.oracle.com/javase/tutorial/java/generics/…
【解决方案2】:

要使myClass.method(); 合法,MyClass 类或接口必须声明method 方法。没错。

getClass 结果与ArrayList&lt;Integer&gt;ArrayList&lt;String&gt; 相等的原因是,在运行时,类型擦除会擦除泛型类型参数,在这两种情况下留下ArrayList,当然它们都是相等的。只有编译器才能区分ArrayList&lt;Integer&gt;ArrayList&lt;String&gt;。 JVM 从来不知道泛型,因此类型擦除恰好与泛型前(Java 1.5 前)应用程序向后兼容。

【讨论】:

  • 这不是为了类型安全目的,而是为了向后兼容 Java 1.4.2或更早版本的目的。
  • 顺便说一下,ArrayList 或其他 Java SE 类/接口没有权威源代码。请注意,您展示的是 OpenJDK 6 源代码(不知道为什么不是 OpenJDK 7),我敢打赌这不会用于 JRockit 或 IBM JVM 实现(仅举几例)。
  • @LuiggiMendoza True 关于类型安全/向后兼容性;我最初的修订不清楚。
【解决方案3】:

搜索类型擦除。开始你可以参考http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-14
    • 2023-04-07
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    相关资源
    最近更新 更多