java - 类型转换为泛型

标签 java generics

我相当了解泛型。根据我的理解,调用泛型类或方法时必须提供 Type 参数。例如:

List<String> strings = new ArrayList<String>();

但是我在理解以下代码时遇到一些困难。

public static <T extends Employee> T findById(@NonNull factory, int employeeId) {
    return (T) factory.findEmployeeById(employeeId);
}

员工有很多子类型。例如:

public class Programmer extends Employee {}

public class Engineer extends Employee {}

通过使用这个 findById 方法,我可以成功实现如下所示。

Programmer employee = EmployeeUtils.findById(factory, 2);

在上面的findById方法中,如何知道T的类型是什么?仅暗示它是 Employee 的子类型。它无法知道自己是程序员还是工程师。 Java 编译器如何在运行时成功地将类型转换为具体子类(ProgrammerEngineer)?

如果您想推荐有关 Java 泛型的内容,请指导我进一步阅读。

最佳答案

How can Java compiler successfully do the type cast at the runtime to Programmer (or Engineer)?

Java 编译器不进行强制转换。

强制转换只是对编译器说的一种方式:“我比你知道的更多;相信我。”编译器仍然会尝试阻止您执行它可以确定绝对不安全的强制转换(例如将 String 强制转换为 Integer),但通过强制转换,您将对类型负责远离编译器的安全。仅当您知道(通过代码/系统的语义)它是安全的时,您才应该进行强制转换。

这种特殊模式对于从存储库检索异构实体很常见;但它不是类型安全的。

该方法的调用者有责任仅使用“正确类型的 ID”调用该方法。问题是代码中没有任何迹象表明它可能会失败。

这种模式总是让我烦恼的原因是它隐藏了问题发生的实际位置。回想一下,Java 泛型基本上只是强制转换的省略。这意味着该方法“实际上”看起来像这样:

public static Employee findById(@NonNull factory,int employeeId) {
  // There is actually no cast here!
  return factory.findEmployeeById(employeeId);
}

调用站点如下所示:

// The cast is here instead!
SpecificEmployeeType e = (SpecificEmployeeType) findById(someId);

(尝试像这样显式编写代码,并将字节码与通用版本进行比较)

因此,虽然您可以抑制 findById 方法中的警告,但实际的异常发生在调用站点。因此,警告​(表明可能出现问题)出现在错误的位置。

如果您在不使用泛型的情况下显式编写它,您可以准确地看到问题实际发生的位置。

人们经常说他们希望以这种方式使用泛型,因为它“更干净”:您不需要在每个调用站点进行显式强制转换或抑制。就我个人而言,我想要那里有额外的东西:它看起来像是那里正在发生危险的事情(确实有!),所以你知道你需要格外小心。

<小时/>

还值得指出的是,您不应该@SuppressWarnings("unchecked")添加到您的代码中 - 无论是在findById中方法,或在调用站点 - 除非您可以绝对确定强制转换是安全的。

与强制转换一样,警告抑制是对编译器无法证明的事情承担责任的一种方式。当然,通过抑制警告,您只是忽略了编译器试图提供的帮助。与任何警告性建议一样,您可以随意忽略它,但在没有完全了解后果的情况下这样做是鲁莽的。

关于java - 类型转换为泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44601402/

相关文章:

java - 如何声明和实例化一个新的通用数组?

templates - Kotlin看似递归的编译模板类型

java - Android Studio 没有从 editText 框获取值(value)

linear-regression - Java非负多元线性回归库

java - ( Play 2.1.3)最小化模型中的代码。静态方法的泛型

generics - VHDL:使用整数通用的长度来确定选择行的数量

c# - 自动将 Interface<T> 解析为 StructureMap 中的 Implementation<T>(仅泛型类型 T 不同)

java - python中java的 'constructor-overload'相当于什么?

java - 如何在 Java 中处理命令行参数

java - 避免在循环中实例化对象 - SONAR 合规性