为什么“t instanceof T”是当 T 是类型参数且 t 是变量时不允许?

发布于 2024-12-24 18:21:53 字数 453 浏览 2 评论 0原文

Eclipse 表示,由于泛型类型擦除器,类型参数不允许进行 instanceof 操作。

我同意在运行时,不会保留任何类型信息。但请考虑以下类的通用声明:

class SomeClass<T>{
    T t;
    SomeClass(Object o){
        System.out.println(o instanceof T);   // Illegal
    }   
}        

在运行时,不会出现 T!但是如果我实例化这个Integer类型的类,那么对应的对象就会有一个Integer类型的字段t。

那么,为什么我不能用 T 检查变量的类型,在运行时可以用 Integer 替换它。我实际上会做类似“o instanceof Integer”的事情。

在什么情况下,允许带有类型参数的instanceof会引起麻烦而被禁止?

Eclipse says that the instanceof operation is not allowed with Type Parameter due to generic type eraser.

I agree that at runtime, no type information stays. But consider the following generic declaration of class :

class SomeClass<T>{
    T t;
    SomeClass(Object o){
        System.out.println(o instanceof T);   // Illegal
    }   
}        

At runtime, no T would be present! But if I instantiate this class of type Integer, then the corresponding object will have a field t of type Integer.

Then, why can't I check the type of a variable with T which can be replaced by Integer at runtime. And I would be actually doing something like "o instanceof Integer".

Under which cases, allowing instanceof with a Type Parameter can cause trouble so that it is prohibited?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

陌上芳菲 2024-12-31 18:21:53

但是如果我实例化这个 Integer 类型的类,那么
相应的对象将有一个 Integer 类型的字段 t

不,不会。它将有一个对象类型的字段。只是每次访问它时,它都会被转换为整数。

考虑以下代码:

SomeClass<Integer> c = new SomeClass<Integer>();
SomeClass untyped = (SomeClass)c; // Which type was it?
SomeClass<String> stringTyped = (SomeClass<String>)untyped; // Now it's STRING??

有效。给你一堆编译器警告,但有效。因为字段 T 实际上是 Object 类型,并且可以转换为任何类型。

But if I instantiate this class of type Integer, then the
corresponding object will have a field t of type Integer

No, it won't. It will have a field of type Object. Just everytime you access it, it will be cast to an Integer.

Consider the following code:

SomeClass<Integer> c = new SomeClass<Integer>();
SomeClass untyped = (SomeClass)c; // Which type was it?
SomeClass<String> stringTyped = (SomeClass<String>)untyped; // Now it's STRING??

Works. Gives you a bunch of compiler warnings, but works. Because the field T is actually of type Object and can be cast to anything.

辞慾 2024-12-31 18:21:53

如果运行时需要T,则需要在运行时提供它。这通常通过传递 Class来完成。 T 必须是哪个。

class SomeClass<T> {
    final T t;

    public SomeClass(Class<T> tClass, T t) {
        if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass);
        this.t = t;
    }

    private SomeClass(T t) {
        this.t = t;
    }

    public static <T> SomeClass<T> of(Class<T> tClass, T t) {
        if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass);
        return new SomeClass(t);
    }
} 

// doesn't compile
SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, "one");

Class clazz = Integer.class;
// compiles with a warning and throws an IAE at runtime.
SomeClass<Integer> intSomeClass = (SomeClass<Integer>) SomeClass.of(clazz, "one");

// compiles and runs ok.
SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, 1);

If you need T at runtime, you need to provide it at runtime. This is often done by passing the Class<T> which T has to be.

class SomeClass<T> {
    final T t;

    public SomeClass(Class<T> tClass, T t) {
        if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass);
        this.t = t;
    }

    private SomeClass(T t) {
        this.t = t;
    }

    public static <T> SomeClass<T> of(Class<T> tClass, T t) {
        if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass);
        return new SomeClass(t);
    }
} 

// doesn't compile
SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, "one");

Class clazz = Integer.class;
// compiles with a warning and throws an IAE at runtime.
SomeClass<Integer> intSomeClass = (SomeClass<Integer>) SomeClass.of(clazz, "one");

// compiles and runs ok.
SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, 1);
他是夢罘是命 2024-12-31 18:21:53

编译语句o instanceof T后将是o instanceof Object并且因为所有类型都派生自Object,所以它的计算结果将始终为true。允许此类测试会给出假阳性结果

After compiling statement o instanceof T would be o instanceof Object and because all types derives from Object, it will always evaluate to true. Allowing this kind of tests would give false positive results

爱格式化 2024-12-31 18:21:53

由于类型擦除,这永远不会起作用。在运行时,您只知道您的类有一个类型参数 T,但不知道它对于给定实例是哪种类型。所以你一开始就无法确定一个对象是否是T类型,因为你不知道T是什么,而不是因为它会引起某种麻烦。

如果您需要执行此类运行时检查,请将类型标记显式传递给您的对象:

SomeClass(Object o, Class<T> type) {
    System.out.println(type.isInstance(o));
}

Because of type erasure, this never works. At runtime, you only know that your class has a type parameter T, but not which type it is for a given instance. So you can't determine whether an object is of type T to begin with, because you don't know what T is, not because it would cause some sort of trouble.

If you need to do this sort of runtime check, pass a type token to your object explicitly:

SomeClass(Object o, Class<T> type) {
    System.out.println(type.isInstance(o));
}
失退 2024-12-31 18:21:53

但是如果我实例化这个Integer类型的类,那么对应的对象就会有一个Integer类型的字段t。

事实上,不会。它有一个 Object 类型的字段 t。正如您所说,泛型几乎完全是语法糖(例外是,当您扩展泛型类并指定类型参数时,该类型仍作为类文件中的元数据)。

But if I instantiate this class of type Integer, then the corresponding object will have a field t of type Integer.

Actually, it wouldn't. It'd have a field t of type Object. As you've said, generics are almost entirely syntactic sugar (the exception is that when you extend a generic class and specify a type parameter, the type remains as metadata in the class file).

清眉祭 2024-12-31 18:21:53

泛型类型参数在运行时是未知的,因此没有可以比较的类。 T 仅在编译时才知道。泛型只会帮助开发人员更轻松地编写代码。但在运行时,参数只是 Object 实例。

The generic type arguments are not known at runtime, so there is no class you can compare with. T is only known at compile time. Generics do only help the developer to write code easier. But at runtime, the arguments are just Object instances.

输什么也不输骨气 2024-12-31 18:21:53

Java使用“Erasure”来实现泛型,它可以在“COMPILE TIME”中检查类型并删除类型参数信息,在“RUN TIME”中只会有<类型参数的strong>BOUNDARIES,因此不会出现“Integer”之类的内容

Java realize its Generics using "Erasure", it can check the type and delete the type parameter information in "COMPILE TIME", in "RUN TIME" there will only the BOUNDARIES of the type parameters, so there will not be any thing like "Integer"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文