【问题标题】:Why does LinkedHashSet<E> extend HashSet<e> and implement Set<E>为什么 LinkedHashSet<E> 扩展 HashSet<e> 并实现 Set<E>
【发布时间】:2011-01-11 00:49:08
【问题描述】:

今天打开一个LinkedHashSet源码,发现了一些有趣的东西:

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {

问题是:当 HashSet 已经是 Set 的时候,为什么还要“extends HashSet”和“implements Set”?

【问题讨论】:

    标签: java collections hashtable


    【解决方案1】:

    我问过 Josh Bloch,他告诉我这是一个错误。很久以前,他曾经认为它有一些价值,但他后来“看到了光明”。显然 JDK 维护者不认为这值得以后退出。

    【讨论】:

      【解决方案2】:

      他们需要明确写implements Set&lt;E&gt;。他们这样做是为了便于阅读。

      【讨论】:

      • “可读性”这就是你犯错、保住工作和在绩效评估中摸索的方式
      【解决方案3】:

      还有一个原因;考虑以下 java 程序:-

      package example;
      
      import java.io.Serializable;
      import java.util.Arrays;
      
      public class Test {
      
       public static interface MyInterface {
        void foo();
       }
      
       public static class BaseClass implements MyInterface, Cloneable, Serializable {
      
        @Override
        public void foo() {
         System.out.println("BaseClass.foo");
        }
       }
      
       public static class Class1 extends BaseClass {
      
        @Override
        public void foo() {
         super.foo();
         System.out.println("Class1.foo");
        }
       }
      
       static class Class2 extends BaseClass implements MyInterface, Cloneable,
         Serializable {
      
        @Override
        public void foo() {
         super.foo();
         System.out.println("Class2.foo");
        }
       }
      
       public static void main(String[] args) {
      
        showInterfacesFor(BaseClass.class);
        showInterfacesFor(Class1.class);
        showInterfacesFor(Class2.class);
       }
      
       private static void showInterfacesFor(Class<?> clazz) {
        System.out.printf("%s --> %s\n", clazz, Arrays.toString(clazz
          .getInterfaces()));
       }
      }
      

      输出以下文本(java 6u16):

      class example.Test$BaseClass --> [interface example.Test$MyInterface, interface java.lang.Cloneable, interface java.io.Serializable]
      class example.Test$Class1 --> []
      class example.Test$Class2 --> [interface example.Test$MyInterface, interface java.lang.Cloneable, interface java.io.Serializable]
      

      注意 Class1 没有定义显式接口,因此 Class#getInterfaces() 不包含这些接口,而 Class2 包含。 this 的使用只有在这个程序中才变得清楚:-

      package example;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      import example.Test.BaseClass;
      import example.Test.Class1;
      import example.Test.Class2;
      
      public class Test2 extends Test {
      
       public static void main(String[] args) {
      
        MyInterface c1 = new Class1();
        MyInterface c2 = new Class2();
      
        // Note the order...
        MyInterface proxy2 = createProxy(c2);
        proxy2.foo();
      
        // This fails with an unchecked exception
        MyInterface proxy1 = createProxy(c1);
        proxy1.foo();
       }
      
       private static <T> T createProxy(final T obj) {
      
        final InvocationHandler handler = new InvocationHandler() {
      
         @Override
         public Object invoke(Object proxy, Method method, Object[] args)
           throws Throwable {
          System.out.printf("About to call %s() on %s\n", method
            .getName(), obj);
          return method.invoke(obj, args);
         }
        };
      
        return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
          .getClass().getInterfaces(), handler);
       }
      }
      

      哪些输出:-

      About to call foo() on example.Test$Class2@578ceb
      BaseClass.foo
      Class2.foo
      Exception in thread "main" java.lang.ClassCastException: $Proxy1 cannot be cast to example.Test$MyInterface
       at example.Test2.main(Test2.java:23)
      

      虽然 Class1 确实隐式实现了 MyInterface,但创建的代理没有。

      因此,如果我们想要创建一个动态代理,它为具有隐式接口继承的对象实现所有接口,那么一般来说,唯一的方法是将超类一直返回到 java.lang.Object,如以及遍历所有已实现的接口及其超类(请记住 Java 支持多个接口继承),这听起来效率不高,但显式命名接口要容易得多(而且更快),因为我认为它们是在编译时设置的.

      那么什么使用反射和代理呢?一个 RMI...

      因此,是的,这是一种方便,但不,它肯定不是多余的:请记住,这些类是由 Josh Bloch 精心设计和实现的,所以我怀疑它们是以这种方式显式编程的,以便代理网络存根和骨架工作和他们一样。

      【讨论】:

      • 旁注:虽然当所有接口都处于同一级别时,它确实使动态代理更容易创建,但您始终可以反射性地遍历类层次结构(很容易,因为 Java 是单继承)并弄清楚父类的所有接口。
      【解决方案4】:

      很好,他们也不需要输入java.io.Serializable

      【讨论】:

        【解决方案5】:

        这是多余的。你可以不用implements Set&lt;E&gt;

        【讨论】:

          【解决方案6】:

          也许这与生成 javadoc 的方式有关。您知道 Java API 如何告诉您实现接口或从其他类继承的所有具体类吗?虽然我同意在运行时它是多余的,但我可以看到这可能如何简化 javadoc 的自动生成。当然,这只是一个疯狂的猜测。

          【讨论】:

          • 其实我觉得你是对的。我已经看到 javadoc 做了“错误”的事情,除非添加了冗余的“实现”,甚至列出接口的顺序也很重要。
          猜你喜欢
          • 1970-01-01
          • 2010-11-24
          • 2010-12-26
          • 1970-01-01
          • 2015-03-26
          • 2016-10-28
          • 1970-01-01
          相关资源
          最近更新 更多