[Java/C#] Множественное наследование
в Java нет множественного наследования.
Обычно все сводится к генерации адаптера, которые перенаправляет вызовы к внутренним объектам.
Имплементят в Java интерфейсы, а не классы, и наимплементить их можно сколько душе угодно.
В Java не то, чтобы нет множественного наследования, просто оно не пущено на самотёк, как это сделано в C++ - в Java всё это просто более регламентировано.
Конкретнее, в Java есть внутренние классы (не путать с вложенными - static - которые соответствуют вложенным классам C++ и эти внутренние классы имеют доступ к элементам внешнего класса. Пусть эти внутренние классы расширяют нужные вам классы. Чем не множественное наследование?
В таком случае, тяжело обеспечить полиморфизм.
А его и так тяжело обеспечить - чью функцию вызывать, если она реализована в нескольких родительских классах? Java просто не делает вид, что это легко.
в зависимости от типа ссылки, естественно
Никто и не говорил, что будет легко.
Но реализация часто встречающейся задачи через генерацию кода - это еще более страшно.
Что прикажешь вызывать?
class A { public: virtual void f {...} };
class B : public A { public: virtual void f {...} };
class C : public A { public: virtual void f {...} };
class D : public B, public C { public: virtual void f {...} } ;
...
ff(A* a) { a->f; ... }
...
main { D d; ff(&d); ... }
P.S. Я задолбался писать "virtual" и "public"!


А тут-то что непонятно?
У тебя в каждом классе определена реализация f, её и вызывать.
Ещё, если только мне не приснилось, в VC++ есть ключевое слово _interface. Может, оно может как-то помочь?
Или все по очереди?
1. как минимум на уровне компилятора можно сказать - что нефига так писать.
2. каким-либо образом сообщить компилятору, какая функция в объединенном классе отвечает за реализацию f
Если ссылка умеет тип D&, то значит D::f вроде логично.
2. А в чём тогда отличие от Java? В обоих случаях ты можешь указать, какую функцию вызывать - я имею в виду, в реализации производного класса.
ОК. Я, похоже, погорячился. Не надо в D никакой функции f. Тогда что?
Как сделано в C++, не помню.
> А в чём тогда отличие от Java?
Тем, что кроме указания в двух-трех местах какую функцию надо вызвать, необходимо писать код для 20-100 методов, которые будут заниматься перенаправлением вызовов.
Мало того, что это бессмысленный ручной код, так это еще влияет и на производительность.
И как это ты его не допустишь? В C++ классы не обязаны наследовать от какого-нибудь класса Object, но любая серьёзная библиотека на C++, которую я видел, почему-то всегда этим занималась. Так что в классе D в качестве f может оказаться любая из функций этого Object.
Именно ее язык и должен поддерживать.
ps
Афаик, в следующих версиях шарпа, mixin-ное наследование поддержать через возможность наследование от шаблонного параметра.
Не дам определять класс D без метода f.
Если честно, я не помню, что это. Только помню, что это отностится именно к этой ситуации.
> Мало того, что это бессмысленный ручной код, так это еще влияет и на производительность.
Ну и что? А механизм виртуальных функций не влияет на производительность?
Так что всё-таки вызываем: B.f или C.f?
выдать какой-нибудь compile error 2474
> но любая серьёзная библиотека на C++, которую я видел, почему-то всегда этим занималась
посмотри тот же ATL, WTL - они все построены на множественном наследовании, но при этом у них нет таких проблем.
выдавать компиле error, пока не будет определена на уровне D функция с именем f.
Будем переопределять все функции Object (если их ~ 10-20)?
Извини, конечно, но я таких умных слов пока не знаю. Если объяснишь, почему у них нет проблем, буду очень признателен.
А как же, иначе поведение класса не определено.
Если такая проблема есть, то обычно это означает, что в программе архитектурная ошибка.
Если класс "стриптезерша, умеющая водить" наследовать от классов Стриптезерша и Водитель тогда и будут появляться такие проблемы.
Стриптезерша, умеющая водить - это человек + навыки стриптиза + навыки вождения.
А вообще, ты наверное имел в виду
class B : virtual public AИначе приведение D* к A* неоднозначно.
class C : virtual public A
Потому что они построены по mixin-технологии.
Забыл эпитет "морально устаревшая"
Так почему не создать class Human, interface AbleToDrive, interface AbleToStriptease? По-моему, это естественно. То, что ты привёл в качестве примера, больше похоже на ребёнка, появившегося в результате посещения Водителем стриптиз-клуба.
Но всё-таки, ответьте, неужели в производном классе надо переопределять все функции Object, как-то: hashValue, toString и т. д.?
Если вы настаиваете, что какой-то mix-in решает все проблемы, ОК, я посмотрю, что это.
Так обычно и делают.
Но дальше делают еще mixin-реализацию Mixin_AbleToDrive и Mixin_AbleToStriptease
И дальше уже "собирают" нужные классы:
class Stripteaser: Human, Mixin_AbleToStriptease{}
class Driver: Human, Mixin_AbleToDrive{}
class StripteaserAndDriver: Human, Mixin_AbleToStriptease, Mixin_AbleToDrive{}
Иначе приходится в каждом из классов заново руками прописывать все эти реализации заявленных интерфейсов
не надо их переопределять, т.к. они есть в единственном экземляре, т.е. mixin-овые реализации абстрактные и не реализуют эти функции.

что такое mixin-наследование?
(сейчас в google посмотрю)
в Java-то множественного наследования все равно нет

Определение я не помню
но на практике это выглядит, примерно, так:
class Human
{
public Coords УвидетьИОпределитьКоординаты(Предмет предмет) {..}
public void ПереместитьСебя(Coords coords);
public void ПереместитьРуку(Coords coords);
public void ПереместитьНогу(Coords coords);
}
interface IDriver
{
void ЗавестиМашину;
}
interface IStripteaser
{
void ПопрыгатьВокругШеста;
}
abstract class Mixin_Driver:
IDriver
{
public Mixin_Driver(Human human)
{
this.human = human;
}
Human human;
public void ЗавестиМашину(Машина машина)
{
human.ПереместиСебя(human.УвидетьИОпределитьКоординаты(машина;
human.ПереместитьРуку(human.Увидеть(Замок;
...
}
}
class Driver:
Human, Mixin_Driver
{
public Driver:Mixin_Driver(this){}
}
т.е. каждый из mixin-реализацией отвечает за реализацию одной небольшой способности (полной или частичной реализации одного определенного интерфейса)
> в Java-то множественного наследования все равно нет
Как раз генераторы кода (эмулирующие множественное наследование) обычно делаются под mixin-подход, т.к. в этом подходе нет тех самых многих проблем, о которых упоминает .
Итого 2 human-а. Разве это хорошо? Human-то, в конце концов, всего один по задумке, если только он не шизоид...
abstract class Mixin_Driver: IDriver
{
...
Human human;
...
}
class Driver: Human, Mixin_Driver { ... }
1 Human, две ссылки
ОК, спасибо всем, похоже на правду...
abstract НедоделанныйStripteaserAndDriver: Human
{
public НедоделанныйStripteaserAndDriver
{
this.driver = new Mixin_Driver(this);
this.stripteaser = new Mixin_Stripteaser(this);
}
Mixin_Driver driver;
Mixin_Stripteaser stripteaser;
}
далее запускается генератор с параметрами: сгенерить класс StripteaserAndDriver, взяв в качестве реализации НедоделанныйStripteaserAndDriver и добавить поддержку интерфейса IDriver и IStripteaser перенаправив вызовы IDriver-а в переменную driver, а вызовы IStripteaser в stripteaser.
Генератор соответственно делает следующих класс:
class StripteaserAndDriver:НедоделанныйStripteaserAndDriver,
IDriver, IStripteaser
{
public void ЗавестиМашину(Машина машина)
{
this.driver.ЗавестиМашину(машина);
}
public void ПопрыгатьВокругШеста
{
this.stripteaser.ПопрыгатьВокругШеста;
}
}
А скажика мне, какой генератор обычно используют разные умные люди? Меня шарп интересует, естественно.
class Mixin_Driver<TBase>:TBase where TBase: Human{...}
class Mixin_Stripteaser<TBase>:TBase where TBase: Human{...}
class StripteaserAndDriver: Mixin_Driver<Mixin_Stripteaser<Human>>>
{
}
свой, кривой

ps
на rsdn-е в рамках проекта R# что-то есть
видел еще пару портов с Java-ы, но ссылки не вспомню.
Для JAVA совсем умные люди mixin аспектами реализуют. Тот же JBoss AOP это позволяет делать изначально, остальные - с некоторым гимором.
Про ATL/WTL вообще не стоит. Там все сплошные синтаксические фокусы, в том числе и множественное наследование, по крайней мере так оно там в основном используется. Таких вещей в Java в принципе нет, как, например, и темплейтов. Так что в случае с ATL/WTL дело не во множественном наследовании. Как показывает практика, что если хорошо спроектировать фреймфорк и API к нему, что без всех эти C++ых штучек можно хорошо обходиться. Правда Java идет по другой дорожке. Всякие AOP, использование аннотаций для генереции кода. Кстати это уже проходили с ATL, не лучший способ решения проблемы кривости API. Но это, как говорится, совсем другая история

Как показывает практика, что если хорошо спроектировать фреймфорк и API к нему, что без всех эти C++ых штучек можно хорошо обходиться.без множественного наследования?
Нет, как раз то, что не связвно с virtual. Это я имел в виду под С++ми штучками. А мн наследование есть d Java. Нет мн наследования реализаций, что и С++ криво реализовано.

А мн наследование есть d Java. Нет мн наследования реализаций, что и С++ криво реализовано.не думаю, что отсутствие решения лучше кривого решения
и всё таки твоя фраза
Как показывает практика, что если хорошо спроектировать фреймфорк и API к нему, что без всех эти C++ых штучек можно хорошо обходиться.не понятна, проблема есть, о ее решении ты ничего не говоришь
какой пример?
А о какой проблеме идет речь? То что Darkgray сказал можно и ручками делать. В какой-нибудь IDEA тебе среда поможет.
как избежать тупого набивания строчек?

то можно замутить в помощь класс
class A implements IA {
public T1 m1 { ... }
...
}
class B implements IB {
public S1 n1 { ... }
...
}
Дальше можно будет наследоваться от HelperBaseFromAB и реализовывать _ia _ib. Если такая гибкость не нужна, то можно сделать поля _ia, _ib.
abstract class HelperBaseForAB implements IA, IB {
protected abstract IA _ia;
protected abstract IB _ib;
public T1 m1 { return _ia.m1; }
...
public S1 n1 { return _ib.n1; }
...
Будут проблемы только, если имена у методов совпадаю. Таких проблем нет в C#.
Правда в Java удобнее перегрузать методы базовых классов с помошью анонимных классов.
В C# так не получится
class ABC extends HelperBaseForAB implements IC {
private IA ia = new A {
public T1 m1 { ... }
...
};
private IB ib = new B {
public S1 n1 { ... }
...
};
...
}

я правильно понял? в этих языках класс Mixin_Driver уже не абстрактный и у него не может быть абстрактных методов, которые можно перегрузить в "наследнике"?
Можно сделать абстрактным, тогда переменные также заводятся в НедоделанномДрайвере, но заполнение этих переменных возлагается на наследников.
Оставить комментарий
6yrop
как имлементят множественное наследование на Java/C#? требуется наследовать классы от 2-4 родителей.есть ли какие паттерны для этого?