2012-05-28 3 views
1

Почему множественное наследование интерфейсов гораздо менее сложно, чем множественное наследование из набора классов? Я застрял на университетском вопросе. ThanksПочему множественное наследование интерфейсов намного менее сложно, чем множественное наследование из набора классов?

+0

Проще для кого? Дизайнер языка? Автор компилятора? Программист использует язык? – NPE

+0

Программист Я предполагаю ... – dewijones92

+0

Наследование нескольких классов не просто сложнее, его невозможно для Java-программиста. – NimChimpsky

ответ

3

Если вы наследуете два метода с одной и той же сигнатурой из разных классов, у вас есть двусмысленность при вызове этого метода. Это можно решить, но это, возможно, грязная ситуация.

Если вы реализуете два интерфейса с одной и той же сигнатурой метода, это не имеет значения, потому что при вызове метода остается только одна реализация.

The diamond problem является продолжением описанной выше проблемы, что делает ситуацию еще более беспорядочной. Эта проблема в основном исчезает при ограничении множественного наследования интерфейсов.

0

Возможно, из-за детализации: вы можете выбрать, какие интерфейсы должны выполнять ваш класс. Если вы расширяете класс, вы автоматически наследуете всю иерархию классов.

+1

Но при реализации интерфейсов вы автоматически получаете всю иерархию интерфейсов. – aioobe

+0

Правда, но это только подписи: P – maksimov

+0

Yup. Но ваш ответ вводит в заблуждение. – aioobe

0

Две возможные причины: 1.) проблема с алмазом (смотрите в Википедии), 2.) Идентификация объекта.

class A { } 
class B { } 
class C : A, B { } 

C * c = new C(); 
A * a = c; 
B * b = c; 

Тогда вы не можете проверить идентификацию объекта, просто проверив a == b. Если вы попытаетесь реализовать эту функцию на языке, вы не можете использовать арифметику простых указателей в фоновом режиме, так что каждый доступ к классу (независимо от многоуровневого наследования) становится намного более дорогостоящим, так как a и b уже не являются обычными указателями ,

Если A и B являются интерфейсами, вы знаете о проблеме (вы знаете, что они будут унаследованы), так что вы можете обрабатывать доступ к интерфейсам иначе, чем доступ к классам.

К слову: множественное наследование классов не , что трудно достичь.

Редактировать: Дополнение. И это вводит концептуальные проблемы:

class C0 { virtual int meth { return 0; } }; 
class C1 : C0 { virtual int meth { return 1; } }; 
class C2 : C1, C0 { } 

Если мы определим

C2 * c2 = new C2; 

Что должно

((C1 *) c2)->meth() 

и

((C0 *) c2)->meth() 

возвращение, чтобы не путать людей? (Изменить: Исправлено в этом примере.)

+0

Что касается вашего последнего вопроса, я не вижу двусмысленности; первый вызов должен возвращать 1, а последний вызов 0. Проблема возникает, если два класса 'C1a' и' C1b' наследуют общий базовый класс 'C0' и переопределяют один и тот же виртуальный член' f', а другой класс 'C2' наследуется от обоих. Если upcasts нужно было выполнить уровень за раз (например, заданный 'C2 it', нельзя было просто сказать' (C0) it', но он должен был бы сказать '(C0) (C1a) it' или' (C0) (C1b) it'), который разрешил бы двусмысленность, но '(C0) (C1a) it' и' (C0) (C1b) он будет вести себя как объекты разных типов или как отдельные объекты. – supercat

+0

Извините, мой пример был настроен на sloppily (без указателей). Однако я вижу концептуальные проблемы, так как вы можете утверждать как для '(C0 *) c2-> meth()' return 0 (так как 'C2' непосредственно наследует от' C0', 'C2' не определяет' meth' , и непонятно, почему «класс C2: C1, C0' должен отличаться от« class C2: C0 »при преобразовании в' C0', или вы можете утверждать, что для 1 (поскольку '(C0 *) (C1 *) c2' не должен отличаться от '(C0 *) c2'). – JohnB

+0

Если язык свободно поддерживает множественное наследование, я не вижу никакого способа поддерживать идентификацию, что' (TFinalType) (TIntermediate1) вещь будет эквивалентной to '(TFinalType) (TIntermediate2) вещь" во всех случаях, когда оба выражения хорошо сформированы и используют только upcasts или self-casts. Следовательно, я не вижу никакого способа избежать требования о том, чтобы значение многократно наследуемого тип класса только повышается до своего непосредственного супертипа и что upcast следует рассматривать как «прямой». Поскольку C0 является прямым супертипом C2, литой следует рассматривать как не проходящий через C1. – supercat

0

Как отмечалось, как минимум в .net, методы интерфейса доступны только для объектов, которые объявлены типами интерфейсов или ограничены. Следовательно, если a C1 имеет способ Foo, а также реализует интерфейсы Intf1 и Intf2, каждый из которых объявляет метод Foo, то в большинстве случаев факт, что существуют три метода с именем Foo, не имеет значения, поскольку они будут существовать в по существу, отдельные домены.

Далее, если интерфейс IDerived1 и IDerived2 наследует IBase, который включает в себя элемент Foo, класс, который реализует оба интерфейса, или интерфейс, который наследует оба интерфейса, будет просто определить одно определение IBase.Foo. Было бы возможно, чтобы IDerived1, IDerived2 и/или интерфейс, полученный из обоих, чтобы объявить свои собственные члены Foo, но любые такие определения будут полностью отделены от IBase.Foo. Передача производного интерфейса на IBase и вызов Foo на нем всегда будут давать тот же метод IBase.Foo, независимо от того, выполняется ли он непосредственно с IBase или один из них переходит к другим интерфейсам. Напротив, если класс происходит из двух других классов, оба из которых переопределяют один и тот же член общего базового класса, неясно, что должно произойти, если кто-то бросает объект производного класса в базовый класс и затем пытается использовать этот член ,

Кстати, есть пара ситуаций, когда могут возникать двусмысленности. Если интерфейс наследует два интерфейса, которые имеют одноименное имя, может потребоваться использование некоторых трюков, чтобы заставить компилятор одобрить одно над другим, или чтобы производный интерфейс добавлял свой собственный член с тем же именем, который теневой оба оригинала. Кроме того, при использовании дженериков можно ограничить общий тип для реализации нескольких интерфейсов, а также наследовать от класса. Когда это будет сделано, существует возможность двусмысленности, если интерфейсы и/или имена участников разделяют класс.

Смежные вопросы