Class.asSubclass 签名

2024-02-15

我的问题非常理论化......这是 Class.asSubclass 的签名(Javadoc http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#asSubclass(java.lang.Class)):

public <U> Class<? extends U> asSubclass(Class<U> clazz)

为什么在返回类型中使用通配符泛型?根据我对泛型的理解,更好的签名可能是:

public <U> Class<U> asSubclass(Class<U> clazz)

因为你肯定可以施展

Class<? extends U>

到更简单的

Class<U>

Bloch 在他的书《Effective Java》中建议(第 137 页,第 28 项):

不要使用通配符类型作为返回类型。它不会为用户提供额外的灵活性,而是会强制他们在客户端代码中使用通配符类型。

这个选择背后的原因是什么?我缺少什么? 预先非常感谢您。

Edit:正如@egelev 所建议的,我确实可以用另一种方式表达我的问题......事实上,“按原样”返回输入参数是没有意义的。所以真正的问题是: 与普通强制转换相比,Class.asSubclass 方法的真正用处是什么?如果出现强制类型转换问题,两者都会抛出 ClassCastException。

也许添加它是为了避免在特定情况下进行未经检查的转换:当您传递 asSubclass 方法的结果时directly到另一个要求约束类型参数的方法,如下所示(摘自Effective Java,第146页):

AnnotatedElement element;
...
element.getAnnotation(annotationType.asSubclass(Annotation.class));

上述方法的签名是:

<T extends Annotation> T getAnnotation(Class<T> annotationClass);

在我看来, asSubclass 方法只是一种在没有适当的编译器警告的情况下进行(事实上!)未经检查的强制转换的方法......

这最终重新提出了我之前的问题:签名

public <U> Class<U> asSubclass(Class<U> clazz)

同样有效(即使很奇怪,我承认)!它将与 getAnnotation 示例完全兼容,并且不会限制客户端代码,迫使其使用毫无意义的通配符泛型。

Edit2:我想我的一般问题已经解决了;非常感谢。如果有人有关于 asSubclass 签名正确性的其他好例子,请将它们添加到讨论中,我想看到一个使用 asSubclass 和我的签名的完整示例显然不工作。


如果返回类型Class<? extends U>。让我们首先尝试理解,getClass签名:

AbstractList<String> ls = new ArrayList<>();
Class<? extends AbstractList> x = ls.getClass();

现在编译器允许我们这样做:

Class<AbstractList> x = ls.getClass();

这是错误的。因为在运行时,ls.getClass将会ArrayList.classand not AbstractList.class。也不可以ls.getClass return Class<ArrayList>因为ls是 AbstractList 类型,而不是 Arraylist

所以编译器现在说 - 好的!我回不去Class<ArrayList>我也不能回来Class<AbstractList>。但因为我知道ls肯定是一个 AbstractList,因此实际的类对象只能是 AbstractList 的子类型。所以Class<? extends AbstractList>是一个非常安全的选择。由于通配符: 你cannot do:

AbstractList<String> ls = new ArrayList<>();
Class<? extends AbstractList> x = ls.getClass();
Class<ArrayList<?>> xx = x;
Class<AbstractList<?>> xxx = x;

同样的逻辑也适用于你的问题。假设它被声明为:

 public <U> Class<U> asSubClass(Class<U> c)

下面将编译:

 List<String> ls = new ArrayList<>();
 Class<? extends List> x = ls.getClass();
 Class<AbstractList> aClass = x.asSubclass(AbstractList.class); //BIG ISSUE

Above aClass在运行时是Class<Arraylist>并不是Class<AbstractList>。所以这是不应该允许的!!Class<? extends AbstractList>是最好的选择。



我看到这个问题时的第一个想法是,为什么不将其声明为:

 public <U extends T> Class<? extends U> asSubClass(Class<U> c)

对我可以传递的参数进行编译时间限制更有意义。但我认为它不是首选的原因是 - 这会破坏 java5 之前代码的向后兼容性。例如,使用 Java5 之前版本编译的代码(如下所示)将不再编译:asSubClass已声明如上所示。

Class x = List.class.asSubclass(String.class); //pre java5
// this would have not compiled if asSubclass was declared above like

快速检查:

    public static <T, U extends T> Class<? extends U> asSubClaz(Class<T> t, Class<U> c){
        return t.asSubClass(c);
    }
    public static <T, U> Class<? extends U> asSubClazOriginal(Class<T> t, Class<U> c){
        return t.asSubClass(c);
    }
    asSubClazOriginal(List.class, String.class);
    asSubClaz(List.class, String.class); //error. So would have broken legacy code

PS:对于编辑过的问题,为什么asSubClass而不是演员? - 因为演员就是背叛。例如:

List<String> ls = new ArrayList<>();
Class<? extends List> x = ls.getClass();
Class<AbstractList> aClass = (Class<AbstractList>) x;

上面总是会成功,因为泛型被删除了。所以它的类强制转换为一个类。但aClass.equals(ArrayList.class)会给出错误。所以演员肯定是错误的。如果您需要类型安全,您可以使用asSubClaz above

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Class.asSubclass 签名 的相关文章

随机推荐