Java 泛型 - 这种未经检查的强制转换安全吗?

发布于 2024-10-14 14:50:17 字数 1831 浏览 2 评论 0 原文

我有(另一个)未经检查的演员问题。我 90% 确定它是安全的,但我想确保(我正在向正在审查代码的另一位开发人员证明使用 @SupressWarnings 的合理性)

以下模式已由我们的框架:

abstract class Writer<T> {
    Class<T> valueType;

    Writer(Class<T> valueType) {
        this.valueType = valueType;
    }
}
class Cat { }

class CatWriter extends Writer<Cat> {
    CatWriter() {
        super(Cat.class);
    }
}

我还使用 Writer 的子类来编写一个使用泛型的类:

class Color {}
class Green extends Color {}
class Brown extends Color {}

我的编写器类如下所示:

abstract class Bird<C extends Color> {}
class Parrot extends Bird<Green>{}
class Ostrich extends Bird<Brown>{}

class BirdWriter<C extends Color> extends Writer<Bird<C>> {
    BirdWriter(Bird<C> bird) {
        super((Class<Bird<C>>)bird.getClass());
    }
}

我可以在编写器中使用原始类型,但这会发出更多警告。相反,我将泛型包含在 Writer 类中。除了构造函数之外,这在任何地方都很好。我被迫将bird.getClass()(这是一个没有通用签名的类对象)转换为具有通用签名的类对象。 这会在转换上产生未经检查的转换警告,但我相信将结果转换为Class>因为传入参数的 bird 保证是 Bird

测试支持了我的理论,但我想确保我的想法是正确的。 此代码是否有任何不安全的地方?


更新

感谢您的回答。由于这些答案,我意识到我的结构中存在弱点并对其进行了修改。

本质上,Cat 使用一个简单的 Writer,它知道它始终在编写 Cat。就我而言,我有一种可以由动态 Writer 编写的“SmartAnimal”类型,这样我就不必为每个 Animal 创建一个 Writer。

class SmartAnimal {}
class Dog extends SmartAnimal {}
class Horse extends SmartAnimal {}
class SuperHorse extends Horse {}

class DynamicWriter<A extends SmartAnimal> extends Writer<A> {
    DynamicWriter(A smartAnimal) {
        super((Class<A>)smartAnimal.getClass());
    }
}

我再次收到同样的警告,但这似乎更安全。

这样更好吗,安全吗?

I have (another) unchecked cast question. I am 90% sure that it is safe, but I want to make sure (I'm justifying the use of @SupressWarnings to another developer who is reviewing the code)

The following pattern has been set up by our framework:

abstract class Writer<T> {
    Class<T> valueType;

    Writer(Class<T> valueType) {
        this.valueType = valueType;
    }
}
class Cat { }

class CatWriter extends Writer<Cat> {
    CatWriter() {
        super(Cat.class);
    }
}

I am also using a subclass of Writer to write a class that makes use of generics:

class Color {}
class Green extends Color {}
class Brown extends Color {}

My writer class looks like this:

abstract class Bird<C extends Color> {}
class Parrot extends Bird<Green>{}
class Ostrich extends Bird<Brown>{}

class BirdWriter<C extends Color> extends Writer<Bird<C>> {
    BirdWriter(Bird<C> bird) {
        super((Class<Bird<C>>)bird.getClass());
    }
}

I could use raw types in the writer but that gives many more warnings. Instead I include the generics in the Writer class. This is fine everywhere but the constructor. I am forced to cast the bird.getClass() (which is a class object which has no generic signature) to a Class object with a generic signature. This produces an unchecked cast warning on the cast, but I believe it is safe to cast the result to Class<Bird<C>> because the bird that is passed into the parameter is guaranteed to be a Bird<C>.

Testing backs up my theory, but I want to make sure that my thinking is correct. Is there any way in which this code is unsafe?


Update

Thanks for your answers. Because of the answers I realized there was a weakness in my structure and have revised it.

Essentially Cat uses a simple Writer that knows it's always writing a Cat. In my case, I've got a type of "SmartAnimal" that can be written by a dynamic Writer, so that I don't have to create a Writer for every Animal.

class SmartAnimal {}
class Dog extends SmartAnimal {}
class Horse extends SmartAnimal {}
class SuperHorse extends Horse {}

class DynamicWriter<A extends SmartAnimal> extends Writer<A> {
    DynamicWriter(A smartAnimal) {
        super((Class<A>)smartAnimal.getClass());
    }
}

Again, I have the same warning, but this seems to be more safe.

Is this better, and is it safe?

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

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

发布评论

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

评论(3

花海 2024-10-21 14:50:17

你并不完全正确。正确、安全但仍未经检查的转换是 Class>,因为您传递的鸟可能是一只 Owl,它会返回一个 Class,它可以分配给一个 >。彼得·劳瑞(Peter Lawrey)是正确的,因为这是不受控制但安全的。

更新:

不,出于同样的原因它仍然不安全。您仍然可以执行类似 new DynamicWriter(new SuperHorse()); 的操作,它将执行从 ClassClass 的未经检查的转换;。为了安全起见,您需要转换为 Class

You are not entirely correct. The correct, safe, but still unchecked cast is to Class<? extends Bird<C>>, as the bird you are passed could, for instance, be an Owl, which would return a Class<Owl> which could be assigned to a Class<? extends Bird<C>>. Peter Lawrey is correct in the reason that this is unchecked but safe.

Update:

No, it is still not safe for the same reason. You can still do something like new DynamicWriter<Horse>(new SuperHorse()); which would perform an unchecked cast from Class<SuperHorse> to Class<Horse>. To be safe, you need to cast to a Class<? extends A>.

洛阳烟雨空心柳 2024-10-21 14:50:17

由于 ILMTitan 中列出的原因,这肯定是不正确的他的回答。但我会给你一个原因,为什么它也可能不太安全。想象一下,您有一个方法 verifyType() 来确保传入写入的任何内容都是正确的类型:

public void write(T t) {
   if (!verifyType(t)) explode();
   //...do write
}

public boolean verifyType(T t) {
   return valueType.isInstance(t);
}

您希望该方法永远不会失败,对吧?毕竟,您只需确保 tT (因为您有一个 Class),而且我们已经知道了一个T,因为write()只接受一个T?正确的? 错误!您实际上并没有检查t是否是T,而是可能检查T的某种任意子类型。

检查一下如果您声明一个 Writer> 并用 Parrot 实例化它,会发生什么,它将是合法的 执行此调用:

Writer<Bird<Green>> writer;
writer.write(new SomeOtherGreenBird());

您不会期望它会失败。但是,您的值类型仅适用于 Parrot,因此类型检查将会失败。这完全取决于你在作家课上做什么,但要预先警告:你正在踏入危险的水域。声明一个 会防止您犯这样的错误。

It is most certainly not correct, for the reason listed by ILMTitan in his answer. But I'll give you a reason why it might also not be very safe. Imagine you had a method verifyType() that made sure that anything that was passed in for writing was the correct type:

public void write(T t) {
   if (!verifyType(t)) explode();
   //...do write
}

public boolean verifyType(T t) {
   return valueType.isInstance(t);
}

This method you would expect never to fail, right? After all, you're just ensuring that t is a T (since you have a Class<T>), and we already know it is a T because write() only accepts a T? Right? Wrong! You're not actually checking if t is a T, but potentially some arbitrary subtype of T.

Examine what would happen if you declared a Writer<Bird<Green>> and instantiated it with a Parrot<Green>, it would be legal to do this call:

Writer<Bird<Green>> writer;
writer.write(new SomeOtherGreenBird());

And again you would not expect it to fail. However your value type is for Parrot only, so the type check would fail. It all depends what you're doing in your writer class, but be forewarned: you are treading in dangerous water here. Declaring a Class<? extends T> in your writer would prevent you from making a mistake like this.

陌若浮生 2024-10-21 14:50:17

恕我直言,它是安全的。问题是 getClass() 在运行时返回对象的类,但编译器不理解 getClass() 的作用,也没有一种语法可以执行类似的操作,

Class<this> getClass();

因为它是唯一的示例何时使用它。

IMHO, its safe., The problem is that getClass() returns the class of the object at runtime, but the compiler doesn't understand what getClass() does, nor is there a syntax which does something like

Class<this> getClass();

possibly because its the only example of when it would be used.

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