简体   繁体   中英

Why does LinkedHashSet<E> extend HashSet<e> and implement Set<E>

Opened a LinkedHashSet source code today and found some interesting thing:

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

The question is: why do they need both "extends HashSet" and "implements Set" when HashSet already is the Set?

I've asked Josh Bloch, and he informs me that it was a mistake. He used to think, long ago, that there was some value in it, but he since "saw the light". Clearly JDK maintainers haven't considered this to be worth backing out later.

They didn't need to explicitly write implements Set<E> . They did it for readability.

There is another reason; consider the following java program:-

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()));
 }
}

Which outputs the following text (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]

Notice how Class1 does not have explicit interfaces defined, so the Class#getInterfaces() does not include those interfaces, whereas Class2 does. The use of this only becomes clear in this program:-

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);
 }
}

Which outputs:-

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)

While Class1 does implicitly implement MyInterface, but the created proxy does not.

Hence if we wanted to create a dynamic proxy which implements all interfaces for an object which has implicit interface inheritance then the only way to do it generically would be to walk the superclasses all the way back to java.lang.Object, as well as walking all the implemented interfaces and their superclasses (remember Java supports multiple interface inheritance), which doesn't sound very efficient, while it is much easier (and faster) to explicitly name interfaces as I suppose they are set in at compile time.

So what uses reflection & proxies? RMI for one...

Therefore, yes it is a convenience, but no it is certainly not redundant: remember that these classes were carefully designed and implemented by Josh Bloch, so I suspect that they were explicitly programmed this way so that proxied network stubs and skeletons work as they do.

很好,他们也不需要放置java.io.Serializable

It's redundant. You could do without the implements Set<E> .

Perhaps it has something to do with the way javadoc gets generated. You know how Java API tells you all concrete classes that impement an interface or inherit from other classes? While I agree that at runtime its redundant, I can see how this might ease the automatic generation of javadoc. This is just a wild guess of course.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM