繁体   English   中英

类型转换为通用

[英]Type cast to Generic

我对泛型相当了解。 根据我的理解,在调用泛型类或方法时必须提供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方法中, findById知道T的类型是什么? 仅暗示它是Employee的子类型。 它不知道它是程序员还是工程师。 Java编译器如何在运行时成功将类型转换为具体的子类( ProgrammerEngineer )?

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

在这种情况下,编译器不会检查强制类型转换。 当相应地设置了编译器选项时,您应该得到一条警告,告知您未经检查的T强制转换。

该检查将在运行时执行。 考虑以下代码:

public class GenericTest {
    public abstract static class Employee {}

    public static class Programmer extends Employee {}

    public static class Engineer extends Employee {}

    public static void main(String[] args) {
        Programmer p = getEmployee();
    }

    public static <T extends Employee> T getEmployee() {
        return (T) new Engineer();
    }
}

该代码将带有警告Type safety: Unchecked cast from GenericTest.Engineer to T编译Type safety: Unchecked cast from GenericTest.Engineer to T 在运行时,将发生ClassCastException:

java.lang.ClassCastException:无法将GenericTest $ Engineer强制转换为GenericTest $ Programmer

执行此强制转换时,应确保在运行时类型正确,否则将抛出难看的异常。

泛型在编译时被类型擦除。 因此,当您分析编译的字节码时,您将看到该方法根据其签名返回一个Employee 实际上,上面示例的已编译字节码与以下代码(具有相同的类)所编译的字节码几乎相同:

public static void main(String[] args) {
    Programmer p = (Programmer) getEmployee();
}

public static Employee getEmployee() {
    return new Engineer();
}

希望这将帮助您了解运行时发生的情况。

要进一步阅读,请考虑《 Oracle泛型教程》。

Java编译器如何在运行时成功将类型强制转换为程序员(或工程师)?

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方法,或者在调用的地方-除非你可以绝对肯定的是,中投是安全的。

像强制转换一样,警告抑制是一种对编译器无法证明的事情负责的方法。 当然,通过禁止显示警告,您只是在忽略编译器试图提供的帮助。 与任何警告性建议一样,您可以随意忽略它,但是在不完全了解后果的情况下这样做是很困难的。

返回类型T将为Employee 以下代码确认了这一点。

public class GenericTest {
    public abstract static class Employee {
    }

    public static class Programmer extends Employee {
    }

    public static class Engineer extends Employee {
        void testMethod(){
            System.out.println("Engineer method");
        }
    }

    public static void main(String[] args) {
        getEmployee().testMethod();
    }

    public static <T extends Employee> T getEmployee() {
        return (T) new Engineer();
    }
}

如果尝试运行代码,则会出现编译错误

错误:(28,22)Java:找不到符号符号:方法engineerMethod()位置:类GenericTest.Employee

要解决该错误,您需要向抽象类添加“抽象方法”或“方法”,如下所示:

public abstract static class Employee {
    void testMethod(){};
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM