【问题标题】:Java, declare variable with multiple interfaces?Java,声明具有多个接口的变量?
【发布时间】:2009-07-13 20:25:15
【问题描述】:

在Java中,是否可以声明一个类型为多个接口的字段/变量?例如,我需要声明一个Map,它也是Serializable。我想确保变量引用一个可序列化的映射。 Map 接口没有扩展Serializable,但Map 的大部分实现都是Serializable

我很确定答案是否定的。

跟进:我完全意识到要创建一个扩展MapSerializable 的新界面。这将不起作用,因为现有实现(例如 HashMap)没有实现我的新接口。

【问题讨论】:

    标签: java


    【解决方案1】:

    我对 Brian 的回答投了赞成票,但想补充一点更高层次的想法..

    如果您查看 SDK,您会发现它们很少(如果有的话)传递实际的集合对象。

    原因是这不是一个好主意。收藏品非常不受保护。

    大多数时候,您希望在传递副本之前制作副本并传递副本,这样对集合的任何修改都不会改变依赖它的其他东西的环境。此外,线程成为一场噩梦——即使是同步集合!

    我见过两种解决方案,一种是总是提取一个数组并传递它。 SDK 就是这样做的。

    另一种方法是始终将集合包装在父类中(我的意思是封装,而不是扩展)。我已经养成了这个习惯,这是非常值得的。它并没有真正花费任何成本,因为无论如何您都不会复制所有收集方法(实际上您很少复制它们中的任何一个)。实际上,您最终所做的是将“实用程序”功能从分布在您的代码中的其他类移到包装器类中,这本来应该放在首位。

    任何具有与“method(collection,...)”匹配的签名的方法几乎可以肯定是该集合的成员方法,任何遍历该集合的循环也应该如此。

    我不得不时不时地把它扔掉,因为这是我有一段时间没有得到的东西之一(因为没有人支持这个概念)。它似乎总是会有一些缺点,但做了一段时间并看到它解决的问题和消除的代码,我自己什至无法想象任何可能的缺点,一切都很好。

    【讨论】:

    • 我同意这一点。我不清楚为什么接受任意现有的Maps 是可取的,而不是一些特定于手头代码的语义上有意义的接口。
    【解决方案2】:

    使用一些泛型技巧可以做到这一点:

        public <T extends Map<?,?> & Serializable> void setMap(T map)
    

    上面的代码使用泛型来强制你传递一个实现这两个接口的映射。但是,请注意,这样做的结果是,当您实际将映射传递给它时,它们可能需要标记为可序列化或已经可序列化的映射类型。它也很难阅读。我会记录地图必须是可序列化的并对其执行测试。

    【讨论】:

      【解决方案3】:

      你可以用泛型来做到这一点,但它并不漂亮:

      class MyClass<T,K,V extends Serializable & Map<K,V>> {
      
         T myVar;
      
      }
      

      【讨论】:

      • 可能想在其中添加 K & V。
      • 你在使用它时通常还需要很多丑陋。
      • 是的,它会吸大的,我宁愿自己拔指甲也不愿用我上面写的,但另一个人在这里付钱。
      【解决方案4】:

      没有必要像那样声明字段/变量。特别是因为它只能在运行时进行测试,而不能在编译时进行。如果传递的 Map 没有实现 Serializable,则创建一个 setter 并报告错误。

      建议您创建自己的接口的答案当然不是很实用,因为它们会主动禁止发送 Maps 和 Serializable 但不是您的特殊接口的东西。

      【讨论】:

      • 即使Map 实现Serializable 也不一定意味着它是可序列化的。但是是的,Java 可串行化是 Java 中的一个运行时问题。
      • 不幸的是,对于 Java,这是正确的答案,应该排在首位。
      • 你错过了我回答的重要部分......你接受了地图,然后检查对象是否是可序列化的实例。你当然可以反过来做,但是你会得到一个界面,并不能真正告诉用户你想要什么样的集合。
      • @Tom,我之前的评论是给你的。
      【解决方案5】:
      public interface MyMap extends Map, Serializable {
      }
      

      将定义一个新接口,它是MapSerializable 的联合。

      您显然必须提供一个合适的实现(例如MyMapImpl),然后您可以提供MyMap(或Map,或Serializable,取决于要求)类型的变量引用。

      为了解决您的说明,您不能改造行为(例如可序列化的地图)。您必须拥有接口和一些适当的实现。

      【讨论】:

      • 我假设他想将现有对象分配给变量,例如一个 HashMap 对象。
      • 我认为他希望有一个可序列化的映射作为变量签名,但仍然能够使用 HashMap、TreeMap 等。即 MyMap map = new HashMap();我认为行不通。
      【解决方案6】:

      您可以通过制作自己的接口来实现这一点,它扩展了您想要的接口

      public interface SerializableMap<K, V> extends Map<K, V>, Serializable {
      
      }
      

      【讨论】:

        【解决方案7】:

        如果您想继续使用现有的 Map 实现,您将无法真正做到。

        另一种方法是创建一个辅助类,并添加一个类似这样的方法:

        public static Serializable serializableFromMap(Map<?, ?> map) {
            if (map instanceof Serializable) {
                return (Serializable)map;
            }
            throw new IllegalArgumentException("map wasn't serializable");
        }
        

        【讨论】:

          【解决方案8】:

          不,你几乎需要投射。

          【讨论】:

            【解决方案9】:

            在我的例子中,它只是声明具体类型:

                HashMap<String, String> mySerializableMap = new HashMap<>();
            

            它允许我使用Map 方法(如put)并将映射传递给需要Serializable 的方法,而无需强制转换。当我们学会了面向接口编程时并不完美,但在我所处的情况下对我来说已经足够了。

            如果你真的坚持:如前所述,单独声明一个组合接口并不能解决问题,因为我们已经拥有的具体类没有实现我们的组合接口,即使它们实现了我们组合的两个接口中的每一个。不过,我把它作为第一步。例如:

            public interface SerializableMap<K, V> extends Map<K, V>, Serializable {
                // No new methods or anything
            }
            

            下一步也是声明一个新类:

            public class SerilizableHashMap<K, V> extends HashMap<K, V> implements SerializableMap<K, V> {
                private static final long serialVersionUID = 4302237185522279700L;
            }
            

            该类被声明为实现组合接口,因此可以在需要其中一种类型的任何地方使用。它扩展了一个已经分别实现每个接口的类,因此我们不需要做更多的事情。现在我们得到了你所要求的。使用示例:

            public static void main(String[] args) {
                SerializableMap<String, String> myMap = new SerilizableHashMap<>();
            
                // myMap works as a Map
                myMap.put("colour1", "red");
                myMap.put("colour2", "green");
            
                // myMap works as a Serializable too
                consumeSerializable(myMap);
            }
            
            public static void consumeSerializable(Serializable s) {
                // So something with the Serializable
            }
            

            出于大多数目的,我认为这是矫枉过正,但现在我至少将其作为一种选择。

            链接: What does it mean to “program to an interface”?

            【讨论】:

              猜你喜欢
              • 2020-04-07
              • 2016-11-28
              • 2013-12-25
              • 1970-01-01
              • 1970-01-01
              • 2020-10-30
              • 2015-06-25
              • 2016-01-27
              • 2016-03-17
              相关资源
              最近更新 更多