泛型方法参数行为

时间:2019-01-06 14:11:35

标签: java generics methods collections

请考虑以下通用方法程序:

watch :{
  todos:{
    handler : function (){
      localStorage.todos = JSON.stringify(this.todos);  
    }
  },
  deep : true,
},

通过参考链接Java Generics - Confusing behavior可以推断出这一点,

在第21行中,因为同时使用String和Integer调用了该方法,所以它用T的Object调用该方法。第25行也是如此,因为List的类型也为Object,它用Object的方法调用T的方法。其他类型将产生错误。我的推论正确吗?

我想不到的是关于

1)第28行,当传递字符串和类时,

2)第30行,当通过了两个不同的类时,

3)第34行,当将声明为无类型的List传递为参数时。

任何人都可以分享他们的知识以清除我的理解。任何帮助,将不胜感激!谢谢!

2 个答案:

答案 0 :(得分:1)

  

其他类型的呼叫清单将产生错误。我的推论正确吗?

是;例如,您不能在此处传递List<String>。通常,第二个addList方法的意图是错误的。正确的签名是这样的:

public <T> List<T> addList(T t1, T t2, List<? extends T> list)

请注意? extends语法。如果您从列表中读取了所有内容,则没有理由不添加它。 T的某些特定子类型的列表保证包含Ts。根本就存在差异的原因:如果您想添加此列表怎么办?假设T为Number,您传入了Integer的列表。双是一个数字。使用List<T> list,您可以调用list.add(5.0),它将编译并运行,将双精度数放入整数列表中。使用List<? extends T>时,add调用始终是编译器错误(除非您尝试添加null会起作用)。为了阅读,您会得到T的信息,无论您是在get还是List<? extends T>上调用List<T>,都没关系。

  

1)第28行,当传递字符串和类时,

否,您要传递String类型的实例和GenericTest类型的实例。字符串与GenericTest一样是一个类。字符串不是特殊的。这与第21行中的调用完全相同:您要传递2个表达式;一个对象类型X,另一个对象类型Y。因此,推断T是两个传递的实例之间最特定的共享类型(可能是所谓的lub类型:一组类型)。在第21行的情况下,IntegerString最特定的共享类型就是Object(从技术上讲,实际上是lub类型Object & Serializable,但大多数情况下不是)无关紧要)。在第28行中,它是StringGenericTest之间最具体的共享类型,仍然是Object,没有区别。

  

2)第30行,当通过了两个不同的类时,

见上文;完全一样的情况。 GenericTestGenericMethod之间最具体的共享类型是Object

  

3)第34行,当将声明为无类型的List传递为参数时。

第34行中的表达式lst是“原始”类型ArrayList。当您使用原始类型时,会发生两件事:[1]编译器会警告您您正在使用原始类型,[2]对于涉及任何原始类型的任何调用,几乎所有泛型测试和检查都被禁用,因此,编译器只会让这种情况发生。

请记住,泛型是编译器想象力的体现。他们的意思是让编译器告诉您代码已损坏。就是这样。

答案 1 :(得分:0)

通常,应尽量避免在没有类型参数的情况下声明List。即使您必须执行List<Object>,也更加清楚。 (对您和编译器而言)

第25行将不会使用StringIntegerList<String>进行编译,因为传递List<String>会特别使编译器承担<T>String

List l2 = gm.addList("java", new Integer(42), new ArrayList<Object>()); // compiles

List l2 = gm.addList("java", new Integer(42), new ArrayList<String>()); // does not compile

可以addList的声明更改为:

public <T> List<T> addList(T t, T t1, List<? extends T> l) {

然后两个示例都将编译。 List参数上的类型参数不再固定为严格的T,而是固定为TT的超类型。编译器认为类型参数为Object,因为ObjectString(和其他参数)的超类型,因此代码可以编译。

就编译器而言,

第28行,第30行和第34行返回List<Object>。可以在参数之间共享的唯一通用类型TObject,因此编译器推断TObject

就运行时的JVM而言,由于type erasure而没有类型参数,它们都只是List。类型参数在运行时不存在。