【问题标题】:Java Generics - wildcardsJava 泛型 - 通配符
【发布时间】:2012-06-20 04:54:37
【问题描述】:

我是 Java 新手,我很明显对它如何处理泛型有一些误解,但是阅读教程和搜索 stackoverflow 并没有(至少到目前为止)让我更加清晰我怀疑我在滥用通配符。提醒一下,我有 C++ 背景,所以它处理模板的方式可能影响了我的处理方式。

这是我使用代表性类继承的基本结构

abstract class PacketHeader{
   // some stuff
}

class TypeOfPacketHeader extends PacketHeader{
   // extended stuff
}

abstract class Packet<T extends PacketHeader>{
    T mHeader;
    // some methods treating T as a type of PacketHeader
    // some abstract methods
}

class TypeOfPacket extends Packet<TypeOfPacketHeader>{
    static TypeOfPacket obtain {
        return new TypeOfPacket();
    }
    // overriden abstract functions that call specific TypeOfPacketHeader methods on mHeader
}

interface PacketParser<T extends Packet<? extends PacketHeader>>{
T obtainPacket();
        void parse(T packet);
}

class ImplementedPacketParser implements PacketParser<TypeOfPacket>{
     TypeOfPacket obtainPacket(){
         return TypeOfPacket.obtain();
     }
     void parse(TypeOfPacket packet){
         // code that relies on TypeOfPacket specific functions
     }
}

这似乎都是正确的(或者至少 eclipse 没有抱怨),当我尝试使用它们时似乎出现了问题。我的第一次尝试是:

class User{
    PacketParser mParser;

    User(PacketParser parser){
        mParser = parser;
    }

    void DoSomething(){
         Packet packet = mParser.obtainPacket();
         // do some stuff with the packet
         mParser.parse(packet);
    }
}

并导致原始类型的警告。所以我尝试了......

class User{
    PacketParser<? extends Packet<? extends PacketHeader>> mParser;

    User(PacketParser<? extends Packet<? extends PacketHeader>> parser){
        mParser = parser;
    }

    void DoSomething(){
         Packet<? extends PacketHeader> packet = parser.obtainPacket();
         // do some stuff with the packet
         mParser.parse(packet);
    }
}

但这会导致错误

PacketParser 类型中的方法 parse(capture#9-of ? extends Packet) 不适用于参数 (Packet)

在这一点上,我已经确定我对泛型的工作方式存在误解一些东西,所以我求助于 stackoverflow,希望能指出我出错的地方,也许为我指明正确的方向。

【问题讨论】:

  • 恕我直言,不要用泛型来描述一切,因为这是一条通往混乱的捷径。尽量保持它尽可能简单,并且它更有可能有意义。 ;)
  • 好吧,至少对我来说,现在弄清楚设计中这些拐点的架构似乎要容易得多,而不是尝试只为一个案例开发,然后将它们硬塞进去稍后。

标签: java generics bounded-wildcard


【解决方案1】:

在您的User 代码中,编译器不知道mParser 字段上的? extends Packet&lt;? extends PacketHeader&gt;Packet&lt;? extends PacketHeader&gt; packet 局部变量的类型相同。

您需要通过使User泛型来绑定将要使用的数据包类型:

class User<T extends Packet<?>> {
    PacketParser<T> mParser;

    User(PacketParser<T> parser){
        mParser = parser;
    }

    void DoSomething(){
         T packet = parser.obtainPacket();
         // do some stuff with the packet
         mParser.parse(packet);
    }
}

// and then when instantiating your User, specify the packet type:
new User<TypeOfPacket>(new ImplementedPacketParser())

现在编译器知道它看到的Ts 代表Packet&lt;?&gt; 的同一类型,而每次看到? extends Packet&lt;?&gt; 可能是Packet&lt;?&gt; 的不同子类型。

编辑: 附加说明:PacketParser 接口声明中不需要“? extends PacketHeader”,因为Packet 类已经将其T 限制为PacketHeader 和它的子类型。

// ? is implicitly constrained to "extends PacketHeader" because of
// the way Packet's generic is defined
interface PacketParser<T extends Packet<?>> {

【讨论】:

  • 我认为我明白现在发生了什么,感谢您指出隐含的约束。希望由于数据包是从 mParser 获得的,因此编译器能够弄清楚它,但也许这太过分了(我当然不想尝试编写可以做到这一点的编译器.. .)
  • 现在似乎已经全部实现和编译,没有错误或警告。必须在上面的图层上进行一些调整,但还不错。谢谢你!
  • 没问题,很高兴能帮上忙!
【解决方案2】:

我对你的代码做了一些修改。它现在正在编译。尝试使用它。

   abstract class PacketHeader {
    // some stuff
   } 
   class TypeOfPacketHeader extends PacketHeader {
        // extended stuff
    }

    abstract class Packet<T extends PacketHeader> {
        T mHeader;
        // some methods treating T as a type of PacketHeader
        // some abstract methods
    }

    class TypeOfPacket extends Packet<TypeOfPacketHeader> {
        static TypeOfPacket obtain() {
            return new TypeOfPacket();
        }
        // overriden abstract functions that call specific TypeOfPacketHeader
        // methods on mHeader
    }

    interface PacketParser<T extends Packet<? extends PacketHeader>> {
        T obtainPacket();

        void parse(T packet);
    }

    class ImplementedPacketParser implements PacketParser<TypeOfPacket> {
        public TypeOfPacket obtainPacket() {
            return TypeOfPacket().obtain();
        }

        private TypeOfPacket TypeOfPacket() {
            // TODO Auto-generated method stub
            return null;
        }

        public void parse(TypeOfPacket packet) {
            // code that relies on TypeOfPacket specific functions
        }
    }

您的用户类。

class User{
    private PacketParser mParser;

    User(PacketParser parser) {
        mParser = parser;
    }

    void DoSomething() {
        Packet packet = mParser.obtainPacket();
        // do some stuff with the packet
        mParser.parse(packet);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-12
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多