为什么LinkedHashSet <e>扩展HashSet <e>并实现Set <e> </e> </e> </e>

时间:2010-01-29 21:14:38

标签: java collections hashtable

今天打开了LinkedHashSet源代码,发现了一些有趣的事情:

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

问题是:当HashSet已经是Set时,为什么他们需要“extends HashSet”和“implements Set”?

6 个答案:

答案 0 :(得分:31)

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

答案 1 :(得分:6)

他们没有需要来明确地写implements Set<E>。为了便于阅读,他们这样做了。

答案 2 :(得分:4)

还有另一个原因;考虑以下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则不包括。只有在这个程序中才能使用它: -

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支持多个接口继承),这听起来效率不高,而显式命名接口则更容易(也更快),因为我认为它们是在编译时设置的

那么什么使用反射&amp;代理?一个人的RMI ......

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

答案 3 :(得分:2)

好的捕获,他们也不需要放java.io.Serializable

答案 4 :(得分:1)

这是多余的。你可以不用implements Set<E>

答案 5 :(得分:1)

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