簡體   English   中英

為什么 LinkedHashSet<E> 擴展哈希集<e>並實現 Set<E>

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

今天打開一個LinkedHashSet的源碼,發現了一些有趣的東西:

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

問題是:當 HashSet 已經是 Set 時,為什么他們需要“extends HashSet”和“implements Set”?

我問過 Josh Bloch,他告訴我這是一個錯誤。 很久以前,他曾經認為它有一些價值,但后來他“看到了曙光”。 顯然 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)

雖然 Class1 確實隱式實現了 MyInterface,但創建的代理沒有。

因此,如果我們想創建一個動態代理,它為一個具有隱式接口繼承的對象實現所有接口,那么通用的唯一方法就是將超類一直走回到 java.lang.Object,以及走所有實現的接口及其超類(請記住 Java 支持多接口繼承),這聽起來效率不高,但顯式命名接口要容易得多(也更快),因為我認為它們是在編譯時設置的。

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

因此,是的,這是一種方便,但它肯定不是多余的:請記住,這些類是由 Josh Bloch 精心設計和實現的,所以我懷疑它們是以這種方式顯式編程的,以便代理網絡存根和骨架可以像它們一樣工作.

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

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

也許它與生成 javadoc 的方式有關。 您知道 Java API 如何告訴您實現接口或從其他類繼承的所有具體類嗎? 雖然我同意在運行時它是多余的,但我可以看到這將如何簡化 javadoc 的自動生成。 這當然只是一個瘋狂的猜測。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM