Почему LinkedHashSet <E> расширяет HashSet <e> и реализует Набор <E>

Открыл исходный код LinkedHashSet сегодня и нашел некоторую интересную вещь:

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

Вопрос: то, почему им нужно и, "расширяется, HashSet" и "реализует Набор", когда HashSet уже является Набор?

26
задан zeroed 29 January 2010 в 23:30
поделиться

6 ответов

Я спросил Джоша Блоха, и он сообщил мне, что это была ошибка. Когда-то давно он думал, что в этом есть какая-то польза, но с тех пор он "прозрел". Очевидно, сопровождающие JDK не посчитали, что от этого стоит отказаться впоследствии.

31
ответ дан 28 November 2019 в 07:16
поделиться

Это избыточно. Вы можете сделать без настроек , установленных .

1
ответ дан 28 November 2019 в 07:16
поделиться

Хороший улов, им тоже не нужно было ставить java.io.Serializable.

2
ответ дан 28 November 2019 в 07:16
поделиться

Есть еще одна причина; Рассмотрим следующую программу 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 не имеет явных интерфейсов, которые не определены, поэтому класс # Получает () не включает эти интерфейсы, тогда как 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 для одного ...

Следовательно, да, это удобство, но нет, это, безусловно, не избыточно: помните, что эти классы были тщательно разработаны и реализованы Джошем Блохом, поэтому я подозреваю, что они были явно запрограммированы таким образом, чтобы Прикрепленные сетевые заглушки и скелеты работают как они.

4
ответ дан 28 November 2019 в 07:16
поделиться

Возможно, это как-то связано со способом генерации javadoc. Знаете, как Java API сообщает вам все конкретные классы, которые имплицируют интерфейс или наследуют от других классов? Хотя я согласен с тем, что во время выполнения он избыточный, я вижу, как это может облегчить автоматическую генерацию javadoc. Это просто дикая догадка, конечно.

1
ответ дан 28 November 2019 в 07:16
поделиться

Вам не обязательно приводить, вы можете просто рассматривать SQL Date как util Date:

java.sql.Date sqlDate = new java.sql.Date(whenever);
java.util.Date utilDate = sqlDate;

Компилирует и работает просто нормально.

-121--3518760-

Это отличная разметка. Хорошее семантическое использование всех элементов. И красивое использование комментариев. (Да, я видел, что на это уже ответили и проголосовали правильно, но я новый и ищу некоторые точки, если бы я был здесь первым, wammo!)

-121--1010843-

Им не требуется для явной записи реализует Set < E > . Они сделали это для удобочитаемости.

6
ответ дан 28 November 2019 в 07:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: