【问题标题】:Java Generics enforcing compatible wildcards强制兼容通配符的 Java 泛型
【发布时间】:2015-11-21 12:22:38
【问题描述】:

我有这些课程。

class RedSocket {}
class GreenSocket {}
class RedWire {}
class GreenWire {}

我有一个使用 2 个泛型类型的类

public class Connection<W, S> {}

其中 W 是 Wire 类型,S 是 Socket 类型。

我正在尝试强制执行编译时检查,以确保套接字和电线具有相同的颜色。

我试过这样做:

public class Connection<W extends Wire & Color, S extends Socket & Color> {}

interface Color {}

interface Red extends Color {}
interface Green extends Color {}

interface Socket {}
interface Wire {}

class RedSocket implements Socket, Red {}
class GreenSocket implements Socket, Green {}
class RedWire implements Wire,  Red {}
class GreenWire implements Wire, Green {}

但这并不能真正确保使用的 Color 对于两种泛型类型都是相同的,并且仍然允许我这样做:

public class Connection<W extends Wire & Color, S extends Socket & Color> {
    public static void main(String[] args) {
        new Connection<RedWire, GreenSocket>();
        new Connection<GreenWire, RedSocket>();
    }
}

(Radiodef here 出色地解释了为什么会发生这种情况)

如何强制编译时检查以确保插座和电线具有相同的颜色?

【问题讨论】:

  • 可能没有人关心,但是您有什么理由选择MQ 作为Wire & Socket 的类型吗?
  • 啊!我原来的程序有不同的实体。我改变了其他一切,但忘记改变这些。谢谢指出,我改一下。 :)

标签: java generics


【解决方案1】:

尝试适当地混入像“颜色”这样的任意元素是整个继承与组合的典型触发因素。 is-a vs has-a 辩论。对于像 Java 这样的语言的普遍看法是,在大多数情况下,人们应该更喜欢组合而不是继承,以避免出现棘手的通配符之类的事情。其他语言可能会提供更多面向方面的编程方式

虽然其他答案可能会帮助您获得正确的泛型,但我建议您阅读 wikipedia page on the subject 并考虑您是否真的需要在编译时强制执行颜色匹配,或者运行时构造函数检查是否可以完成这项工作。

【讨论】:

    【解决方案2】:

    作为Tagir Valeev's answer 的一个小变种:您可以摆脱Connection 类的第三个泛型参数,通过使其构造函数private(或者可能是包可见),并提供一个工厂方法来创建Connection 实例确保Color 类型对于给定的Wire- 和Socket 类型是相同的:

    class Connection<
        W extends Wire<? extends Color>, 
        S extends Socket<? extends Color>> 
    {
        static <C extends Color, 
            W extends Wire<C>, 
            S extends Socket<C>> Connection<W, S> create()
        {
            return new Connection<W, S>();        
        }
    
        // Private constructor
        private Connection() {}
    }
    
    interface Color {}
    
    interface Red extends Color {}
    interface Green extends Color {}
    
    interface Socket<C extends Color> {}
    interface Wire<C extends Color> {}
    
    class RedSocket implements Socket<Red> {}
    class GreenSocket implements Socket<Green> {}
    class RedWire implements Wire<Red> {}
    class GreenWire implements Wire<Green> {}
    
    public class CompatibleGenericsTest
    {
        public static void main(String[] args)
        {
            Connection<RedWire, RedSocket> c0 = Connection.create(); // ok
            Connection<GreenWire, GreenSocket> c1 = Connection.create(); // ok
            Connection<GreenWire, RedSocket> c2 = Connection.create(); // error
        }
    }
    

    【讨论】:

    • 虽然定义有点复杂,但我会投票支持这种方法,因为 API 的使用变得更加简洁
    • 我以前做过类似的事情,我更喜欢这种方法,但需要注意的是它会使错误(与 Tagir 的解决方案相比)更难阅读。
    【解决方案3】:

    似乎用颜色参数化SocketWire比较好:

    interface Socket<C extends Color> {}
    interface Wire<C extends Color> {}
    
    class RedSocket implements Socket<Red> {}
    class GreenSocket implements Socket<Green> {}
    class RedWire implements Wire<Red> {}
    class GreenWire implements Wire<Green> {}
    

    这样您可以向Connection 引入一个更通用的参数:

    public class Connection<C extends Color, M extends Wire<C>, Q extends Socket<C>> {...}
    

    并像这样使用它:

    new Connection<Red, RedWire, RedSocket>(); // ok
    new Connection<Green, GreenWire, GreenSocket>(); // ok
    new Connection<Green, GreenWire, RedSocket>(); // error
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多