如何解决 Java 泛型通配符/抽象歧义问题?

发布于 2024-09-26 10:20:00 字数 1618 浏览 4 评论 0 原文

下面的代码是我的项目正在使用的模式的简化版本。我们使用的标准模式是为每个对象类型都有一个 Writer。对于一种抽象类型的子类型(在本例中为 Animal),我希望使用枚举来查找正确的作者。

abstract class Writer<T> {
    abstract void write(T value);
}

abstract class Animal {
    abstract AnimalType getType();
}

class Cat extends Animal {
    AnimalType getType() { return AnimalType.CAT; }
}

class CatWriter extends Writer<Cat> {
    void write(Cat value) { }
}

// The AnimalType stores a reference to the correct writer for the Animal subclass
enum AnimalType {
    CAT(new CatWriter());

    Writer<? extends Animal> writer;
    Writer writerThatWorksWithWarning;
    Writer<Animal> writerThatWorksButCantBeAssigned;

    AnimalType(Writer<? extends Animal> writer) {
        this.writerThatWorksWithWarning = writer;
        this.writer = writer;

        // ERROR: Incompatible Types
        this.writerThatWorksButCantBeAssigned = writer;

    }
}

示例用例:

class Test {
    public static void main(String... args) {
        Animal value = new Cat();

// ERROR: write (capture<? extends Animal) in Writer cannot be applied to (Animal)
        value.getType().writer.write(value);

// WARNING: Unchecked call
        value.getType().writerThatWorksWithWarning.write(value);

// This line works fine here - but can't be assigned above
        value.getType().writerThatWorksButCantBeAssigned.write(value);
    }
}

我认为我的问题与此问题中的问题类似: 带有通配符的 Java 泛型,但是我不知道如何解决它。

我已将收到的内联错误和警告放在评论中。

有什么想法吗?

The code below is a simplified version of the pattern my project is using. The standard pattern we use is to have a Writer for each object type. For the subtypes of one abstract type (in this example Animal), I'd like an enum to serve as a lookup for the correct writer.

abstract class Writer<T> {
    abstract void write(T value);
}

abstract class Animal {
    abstract AnimalType getType();
}

class Cat extends Animal {
    AnimalType getType() { return AnimalType.CAT; }
}

class CatWriter extends Writer<Cat> {
    void write(Cat value) { }
}

// The AnimalType stores a reference to the correct writer for the Animal subclass
enum AnimalType {
    CAT(new CatWriter());

    Writer<? extends Animal> writer;
    Writer writerThatWorksWithWarning;
    Writer<Animal> writerThatWorksButCantBeAssigned;

    AnimalType(Writer<? extends Animal> writer) {
        this.writerThatWorksWithWarning = writer;
        this.writer = writer;

        // ERROR: Incompatible Types
        this.writerThatWorksButCantBeAssigned = writer;

    }
}

Sample use case:

class Test {
    public static void main(String... args) {
        Animal value = new Cat();

// ERROR: write (capture<? extends Animal) in Writer cannot be applied to (Animal)
        value.getType().writer.write(value);

// WARNING: Unchecked call
        value.getType().writerThatWorksWithWarning.write(value);

// This line works fine here - but can't be assigned above
        value.getType().writerThatWorksButCantBeAssigned.write(value);
    }
}

I think that my problem is similar to the problem in this question: Java Generics with wildcard, however I can't tell how to solve it.

I've put the inline errors and warnings I get in the comments.

Any ideas?

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

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

发布评论

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

评论(3

混浊又暗下来 2024-10-03 10:20:00

我认为这里的问题是你不能用枚举来表示类型层次结构,因此无法告诉类型系统 for enum { CAT, DOG; } CAT 的类型应为 CAT extends Animal,而 DOG 的类型应为 DOG extends Animal。那么既然你已经有了一个类层次结构,为什么不使用它呢?即:

public interface Writer<T> {
    public void write(T t);
}


public abstract class Animal<T extends Animal<T>> {
    public abstract Writer<T> getWriter()...
}

public class Cat extends Animal<Cat> {
    @Override
    public Writer<Cat> getWriter()...
}

在我看来,您真正使用枚举的目的更像是 > 的哈希图,有点像内置的单例。您可以做到这一点,但只能通过隐藏类型来实现。

I think the issue here is that you can't represent a type hierarchy with an enum, so there's no way to tell the type system that for enum { CAT, DOG; } the CAT should type to CAT extends Animal and the DOG types to DOG extends Animal. So But since you have a class hierarchy already, why not use that? i.e. something like :

public interface Writer<T> {
    public void write(T t);
}


public abstract class Animal<T extends Animal<T>> {
    public abstract Writer<T> getWriter()...
}

public class Cat extends Animal<Cat> {
    @Override
    public Writer<Cat> getWriter()...
}

It seems to me that what you're really using the enum for is something more like a hashmap of <Class, Writer<Class>>, sort of a built in singleton. You can do this, but only by hiding the types.

七月上 2024-10-03 10:20:00

我希望动物不知道作家。他们毕竟是动物。

您可以拥有一个 Map,并且对于其中的每个条目,您可以维护键 Class 和值 WriterX 类型大致相同。我们无法用类型来表达这种关系,因此必须在某些地方进行强制转换。如果查找某个类型失败(例如 Cat),请尝试再次查找其超类型(动物)

类型安全的公共 API 可以设计为假设

static public <T> void registerWriter(Class<T> type, Writer<T> writer)

static public <T> Writer<? super T> getWriter(Class<T> type)

我们没有直接映射到 Cat 的 Writer,但我们确实有一个Writer 对于 Animal,那么将为 Cat.class 返回该 writer。没关系,因为那位作家确实接受所有动物。

可以提供这种方便的方法:

static public static void write(Object obj)

从对象的类型中,可以找到合适的写入者,并且写入者将接受该对象。

I would have animals unaware of writers. they are animals after all.

You can have a Map<Class,Writer>, and for each entry in it, you maintain that the key Class<X> and value Writer<X> are about the same type X. We can't express that relation in types, so casts must be done at some places. If looking up fails for a type (say Cat), try looking up again with its super types (Animal)

A type safe public API can be designed like

static public <T> void registerWriter(Class<T> type, Writer<T> writer)

static public <T> Writer<? super T> getWriter(Class<T> type)

Suppose we don't have a Writer directly mapped to Cat, but we do have a Writer<Animal> for Animal, then that writer will be returned for Cat.class. That is ok, because that writer does accept all animals.

This convenient method can be provided:

static public static void write(Object obj)

from the type of the object, a suitable writer can be found, and the writer will accept the object.

最单纯的乌龟 2024-10-03 10:20:00

试试这个,

enum AnimalType {
    CAT(new CatWriter());

    private Writer<? extends Animal> writer;

    AnimalType(Writer<? extends Animal> writer) {
        this.writer = writer;
    }

    public Writer<Animal> getWriter() {
        return (Writer<Animal>)writer;
    }
}

此外,我不确定你在做什么。但我相信 访问者模式 在这种情况下会派上用场。

上述解决方案有问题,下面的代码会破坏这个问题。

    Animal cat = new Cat();
    Animal dog = new Dog();

    cat.getType().getWriter().write(cat);

    // java.lang.ClassCastException in the write() method's argument
    cat.getType().getWriter().write(dog);

Try this instead,

enum AnimalType {
    CAT(new CatWriter());

    private Writer<? extends Animal> writer;

    AnimalType(Writer<? extends Animal> writer) {
        this.writer = writer;
    }

    public Writer<Animal> getWriter() {
        return (Writer<Animal>)writer;
    }
}

Moreover, I am not sure what are you up to. But I believe that Visitor pattern will come handy in this case.

Problem with the above solution, the code below will break the thing.

    Animal cat = new Cat();
    Animal dog = new Dog();

    cat.getType().getWriter().write(cat);

    // java.lang.ClassCastException in the write() method's argument
    cat.getType().getWriter().write(dog);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文