当在编译时未检测到以下代码中的 ClassCastException 时,即使在使代码成为通用代码之后,泛型的用途是什么?

What is the use of Generics when the ClassCastException in following code is Not detected at compile time, even after making the code Generic?

我读到泛型的全部要点是通过制造更多的错误来增加我们代码的稳定性(本质上是当一个变量被分配一个类型与类型不兼容的值时发生的错误)变量)在编译时可检测。

以下是一个非通用的 class,其中我在语句 B bForStoringReturnedAOne = (B) box.aMethod(c); 处得到一个 RunTimeException,ClassCastException 发生。 我原以为如果我将这段代码设为泛型,则不会发生此 ClassCastException,因为泛型的使用会以某种方式导致导致异常的错误,在编译时可检测到。

所以我也发布了此代码的通用版本。 问题是在编译时没有检测到错误,我在同一条语句中得到了相同的 ClassCastException。 所以问题是有什么区别?泛型有什么帮助?泛型存在的意义何在?即使在使用泛型之后,编译时仍未检测到 bug/exception。

非通用版本:

public class SomeClass {

    private class A {}

    private class B extends A {}

    private class C extends A {}



    private class Box {
        private A aMethod(A a) {
            return a;
        }
    }

    public static void main(String[] args) {
        SomeClass someClass = new SomeClass();

        B b = someClass.new B();

        C c = someClass.new C();

        Box box = someClass.new Box();

        B bForStoringReturnedA = (B) box.aMethod(b);

        B bForStoringReturnedAOne = (B) box.aMethod(c);//*****ClassCastException

    }

}

通用版本:

public class AnotherClass {

    private class A {}

    private class B extends A {}

    private class C extends A {}


    private class Box<T> {
        private T aMethod(T t) {
            return t;
        }
    }

    public static void main(String[] args) {
        AnotherClass someClass = new AnotherClass();

        B b = someClass.new B();

        C c = someClass.new C();

        Box<A> box = someClass.new Box<>();

        B bForStoringReturnedA = (B) box.aMethod(b);

        B bForStoringReturnedAOne = (B) box.aMethod(c);//*****ClassCastException

    }

}

泛型在您给出的示例中完美地完成了它们的工作。

你做了一个 Box<A>,它有 aMethod 接受 A 和 returns A(在类型推断之后)。

您将它作为 B 传递,而方法 returns 将其作为 A 传递。然后你将它转换为 B,因为对象实际上是 B.

然后将 C 传递给它,它也作为 A 返回。然后你将它转换为 B 抛出异常,因为对象实际上不是 B.

这与做的基本相同:

Box<A> box = someClass.new Box<>();

A a1 = box.aMethod(b);
A a2 = box.aMethod(c);

B b1 = (B) a1;
B b2 = (B) a2;

我不明白你期望泛型如何帮助你。


如果你做了 Box<B>:

Box<B> box = someClass.new Box<>();

B b1 = box.aMethod(b); // OK, + no need to cast
B b2 = box.aMethod(c); // Compile time error
error: method aMethod in class Box<T> cannot be applied to given types;
    B b2 = box.aMethod(c); // Compile time error
              ^
  required: B
  found: C
  reason: argument mismatch; C cannot be converted to B
  where T is a type-variable:
    T extends Object declared in class Box
1 error

编译器通过给出错误来正确保证类型安全。

I was expecting that if I make this code Generic, this ClassCastException will not occur, as the use of Generics will somehow make the bug causing the exception, DETECTABLE AT COMPILE TIME.

没有。显式类型转换不是编译时操作,而是 运行 时操作。任何时候你转换一个类型,你基本上是在告诉编译器你比它更了解 运行-time 类型 是什么,并且编译器应该信任你在这件事上。

在这两种情况下,您知道但编译器不知道的信息结果是不正确的。 (当然,是故意的,为了你所说明的内容。)因此例外。

泛型与任何其他泛型一样具有编译时类型安全性 类。他们背后没有魔法,他们无法在未来运行时间错误发生之前检测到它们。

他们所做的是为各种类型提供一种"template"。 Box<A> 是与 Box<B> 完全不同的类型,它本身具有 Java 提供的所有编译类型类型安全性。 Box<> 本身 只是这些类型的模板,但在编译时 specific 类型仍然是已知的。

基本上,Box<A>.aMethod() 的 return 值是 A。它不是动态的,它是不可改变的,它是 A。就像在非通用版本中一样。

泛型给你的是编写这些可重用的 "template" 类型的能力,这些类型可以与许多其他类型组合以形成实际的编译时结果类型。