在所有方法调用中允许类型见证的重点是什么?

Rog*_*gue 25 java generics

假设我们有两种方法,如下所示:

public static <T> T genericReturn() { /*...*/ }
public static String stringReturn() { /*...*/ }
Run Code Online (Sandbox Code Playgroud)

在调用任何方法时,无论是否有任何要求,您都可以提供类型见证:

String s;
s = Internet.<String>genericReturn(); //Type witness used in return type, returns a String
s = Internet.<Integer>stringReturn(); //Type witness ignored, returns a String
Run Code Online (Sandbox Code Playgroud)

但是我根本没有在Java中看到任何实际用途,除非无法推断出这种类型(这通常表明存在更大的问题).此外,当它没有被适当使用时被简单地忽略这一事实似乎违反直觉.那么在Java中有什么意义呢?

ars*_*jii 20

来自JLS§15.2.12.1:

  • 如果方法调用包含显式类型参数,并且成员是泛型方法,则类型参数的数量等于方法的类型参数的数量.

此子句意味着非泛型方法可能适用于提供显式类型参数的调用.实际上,它可能会变得适用.在这种情况下,类型参数将被忽略.

接下来是理由

该规则源于兼容性和可替代性原则的问题.由于接口或超类可以独立于其子类型进行泛化,因此我们可以使用非泛型方法覆盖泛型方法.但是,重写(非泛型)方法必须适用于对泛型方法的调用,包括显式传递类型参数的调用.否则,子类型不能替代其生成的超类型.

沿着这条推理,让我们构建一个例子.假设在Java1.4中,JDK有一个类

public class Foo
{
    /** check obj, and return it */
    public Object check(Object obj){ ... }
}
Run Code Online (Sandbox Code Playgroud)

一些用户编写了一个扩展Foo和覆盖该check方法的专有类

public class MyFoo extends Foo
{
    public Object check(Object obj){ ... }
}
Run Code Online (Sandbox Code Playgroud)

当Java1.5引入泛型时,Foo.check被认为是

    public <T> T check(T obj)
Run Code Online (Sandbox Code Playgroud)

雄心勃勃的后向可比性目标要求MyFoo仍然在Java1.5中编译而不进行修改; 并且MyFoo.check[Object->Object]仍然是最重要的方法Foo.check[T->T].

现在,根据上述理由,自编译以来

    MyFoo myFoo = new MyFoo();

    ((Foo)myFoo).<String>check("");
Run Code Online (Sandbox Code Playgroud)

这也必须编译

    myFoo.<String>check("");
Run Code Online (Sandbox Code Playgroud)

即使MyFoo.check不是通用的.


这听起来像一个延伸.但即使我们购买这个论点,解决方案仍然过于宽泛和过度.JLS可能已经拧紧它,以便myFoo.<String,String>checkobj.<Blah>toString()是非法的,因为类型参数参数数量不匹配.他们可能没有时间去解决它,所以他们只是采取了一条简单的路线.

  • 允许这样做的原因可能类似于可以在实例上调用静态方法的原因.它允许现有代码在方法未泛化后继续编译. (7认同)
  • @MarkoTopolnik:或者,可以允许代码在存在时利用方法的通用版本,同时也可以使用非泛型方法.这可能在涉及继承的场景中有用,或者在某些代码的使用者可能将其与库的不同版本(其中一些将使用泛型,而其中一些不使用)的情况下有用. (2认同)