Вопрос-ответ

Why is super.super.method(); not allowed in Java?

Почему super.super.method(); не разрешен в Java?

Я прочитал этот вопрос и подумал, что это было бы легко решить (не то чтобы это было невозможно без него), если бы можно было написать:

@Override
public String toString() {
return super.super.toString();
}

Я не уверен, что это полезно во многих случаях, но мне интересно, почему это не так и существует ли что-то подобное в других языках.

Что вы, ребята, думаете?

РЕДАКТИРОВАТЬ: Для уточнения: да, я знаю, что это невозможно в Java, и я действительно не скучаю по этому. Я не ожидал, что это сработает, и был удивлен, получив ошибку компилятора. Мне просто пришла в голову идея, и я хотел бы ее обсудить.

Переведено автоматически
Ответ 1

Это нарушает инкапсуляцию. Вы не должны иметь возможности обойти поведение родительского класса. Иногда имеет смысл иметь возможность обойти поведение вашего собственного класса (особенно из того же метода), но не родительского. Например, предположим, что у нас есть базовая "коллекция элементов", подкласс, представляющий "коллекцию красных элементов", и подкласс, представляющий "коллекцию больших красных элементов". Имеет смысл иметь:

public class Items
{
public void add(Item item) { ... }
}

public class RedItems extends Items
{
@Override
public void add(Item item)
{
if (!item.isRed())
{
throw new NotRedItemException();
}
super.add(item);
}
}

public class BigRedItems extends RedItems
{
@Override
public void add(Item item)
{
if (!item.isBig())
{
throw new NotBigItemException();
}
super.add(item);
}
}

Это нормально - RedItems всегда может быть уверен, что все элементы, которые он содержит, красные. Теперь предположим, что мы смогли вызвать super.super.add():

public class NaughtyItems extends RedItems
{
@Override
public void add(Item item)
{
// I don't care if it's red or not. Take that, RedItems!
super.super.add(item);
}
}

Теперь мы можем добавлять все, что захотим, и инвариант в RedItems нарушен.

Имеет ли это смысл?

Ответ 2

Я думаю, что у Джона Скита есть правильный ответ. Я просто хотел бы добавить, что вы можете получить доступ к затененным переменным из суперклассов superclasses путем приведения this:

interface I { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1 { int x = 2; }
class T3 extends T2 {
int x = 3;
void test() {
System.out.println("x=\t\t" + x);
System.out.println("super.x=\t\t" + super.x);
System.out.println("((T2)this).x=\t" + ((T2)this).x);
System.out.println("((T1)this).x=\t" + ((T1)this).x);
System.out.println("((I)this).x=\t" + ((I)this).x);
}
}

class Test {
public static void main(String[] args) {
new T3().test();
}
}

который выдает вывод:

x = 3
super.x= 2
((T2)это).x= 2
((T1) это).x= 1
((I) это).x = 0

(пример из JLS)

Однако это не работает для вызовов методов, потому что вызовы методов определяются на основе типа объекта во время выполнения.

Ответ 3

Я думаю, что следующий код позволяет использовать super.super ... super.method() в большинстве случаев. (даже если это неудобно делать)

Вкратце


  1. создайте временный экземпляр типа-предка

  2. скопируйте значения полей из исходного объекта во временный

  3. вызвать целевой метод для временного объекта

  4. скопируйте измененные значения обратно в исходный объект

Использование :

public class A {
public void doThat() { ... }
}

public class B extends A {
public void doThat() { /* don't call super.doThat() */ }
}

public class C extends B {
public void doThat() {
Magic.exec(A.class, this, "doThat");
}
}


public class Magic {
public static <Type, ChieldType extends Type> void exec(Class<Type> oneSuperType, ChieldType instance,
String methodOfParentToExec)
{
try {
Type type = oneSuperType.newInstance();
shareVars(oneSuperType, instance, type);
oneSuperType.getMethod(methodOfParentToExec).invoke(type);
shareVars(oneSuperType, type, instance);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static <Type, SourceType extends Type, TargetType extends Type> void shareVars(Class<Type> clazz,
SourceType source, TargetType target)
throws IllegalArgumentException, IllegalAccessException {
Class<?> loop = clazz;
do {
for (Field f : loop.getDeclaredFields()) {
if (!f.isAccessible()) {
f.setAccessible(true);
}
f.set(target, f.get(source));
}
loop = loop.getSuperclass();
} while (loop != Object.class);
}
}
Ответ 4

У меня недостаточно репутации, чтобы комментировать, поэтому я добавлю это к другим ответам.

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

Объектно-ориентированное программирование (которым является Java) - это все об объектах, а не о функциях. Если вы хотите программировать, ориентируясь на задачи, выберите C ++ или что-то еще. Если ваш объект не вписывается в свой суперкласс, то вам нужно добавить его в "прародительский класс", создать новый класс или найти другой super, в который он вписывается.

Лично я считаю это ограничение одной из самых сильных сторон Java. Код несколько жесткий по сравнению с другими языками, которые я использовал, но я всегда знаю, чего ожидать. Это помогает с "простой и знакомой" целью Java. На мой взгляд, вызов super.super не является простым или знакомым. Возможно, разработчики чувствовали то же самое?

java