为什么LinkedHashSet <E>扩展HashSet <e>并实现Set <E>
今天打开LinkedHashSet源代码,发现一些有趣的事情:
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable {
问题是:为什么当HashSet已经是Set时,他们同时需要“extends HashSet”和“implements Set”?
我问过乔希·布洛赫,他告诉我这是一个错误。 他很久以前就认为这里有一些价值,但他自从“看见了光明”。 很明显,JDK的维护者并没有考虑到这个值得在后面退出。
他们不需要明确写入implements Set<E>
。 他们这样做是为了可读性。
还有另外一个原因。 考虑下面的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)
尽pipeClass1隐含地实现了MyInterface,但创build的代理却没有。
因此,如果我们想要创build一个dynamic代理,它实现了具有隐式接口inheritance的对象的所有接口,那么通常做到这一点的唯一方法就是将超类一直回溯到java.lang.Object,以及步行所有实现的接口和它们的超类(记住Java支持多接口inheritance),这听起来不是非常有效,但是明显地命名接口要容易得多(而且更快),因为我认为它们是在编译时设置的。
那么什么使用reflection和代理? RMI为一个…
因此,是的,这是一个方便,但不是肯定不是多余的:请记住,这些类是由Josh Bloch精心devise和实现的,所以我怀疑它们是这样明确编程的,这样代理的networking存根和骨架就像它们一样工作。
这是多余的。 你可以做没有implements Set<E>
。
也许这与javadoc生成的方式有关。 你知道Java API如何告诉你所有的具体的类,这些类阻止了一个接口或者从其他类inheritance而来? 虽然我同意在运行时它的冗余,我可以看到这可能会缓解自动生成的javadoc。 这当然是一个疯狂的猜测。
很好的接受,他们不需要把java.io.Serializable
。