Ответ 1
Я спросил Джоша Блоха, и он сообщает мне, что это была ошибка. Раньше он давно думал, что в этом есть какая-то ценность, но он с тех пор "видел свет". Очевидно, что сторонники JDK не посчитали это целесообразным позже.
Открыл исходный код LinkedHashSet сегодня и нашел интересную вещь:
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
Вопрос в том, зачем они нужны и "extends HashSet", и "реализует Set", когда HashSet уже является 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 не имеет явных интерфейсов, поэтому класС# 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 [email protected]
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 для одного...
Поэтому да, это удобство, но нет, конечно, не избыточно: помните, что эти классы были тщательно разработаны и реализованы Джошем Блохом, поэтому я подозреваю, что они были явно запрограммированы таким образом, чтобы проксированные сетевые заглушки и скелеты работали как они это делают.
Это избыточно. Вы можете обойтись без implements Set<E>
.
Возможно, это имеет какое-то отношение к тому, как генерируется javadoc. Вы знаете, как Java API сообщает вам все конкретные классы, которые имитируют интерфейс или наследуют от других классов? Хотя я согласен, что во время выполнения его избыточности я вижу, как это может облегчить автоматическое создание javadoc. Конечно, это просто дикая догадка.
Хороший улов, им не нужно было ставить java.io.Serializable
.