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

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

что такое mixin-наследование?
(сейчас в google посмотрю)
просто множественное наследование, используемое специальным образом, видимо.
в Java-то множественного наследования все равно нет
в Java-то множественного наследования все равно нет

> что такое mixin-наследование?
Определение я не помню
но на практике это выглядит, примерно, так:
т.е. каждый из mixin-реализацией отвечает за реализацию одной небольшой способности (полной или частичной реализации одного определенного интерфейса)
Определение я не помню
но на практике это выглядит, примерно, так:
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-подход, т.к. в этом подходе нет тех самых многих проблем, о которых упоминает .
> в Java-то множественного наследования все равно нет
Как раз генераторы кода (эмулирующие множественное наследование) обычно делаются под mixin-подход, т.к. в этом подходе нет тех самых многих проблем, о которых упоминает .
Итого 2 human-а. Разве это хорошо? Human-то, в конце концов, всего один по задумке, если только он не шизоид...
abstract class Mixin_Driver: IDriver
{
...
Human human;
...
}
class Driver: Human, Mixin_Driver { ... }
1 Human, две ссылки
ОК, спасибо всем, похоже на правду...
в языках, в которых нет множественного наследование делается следующее:
далее запускается генератор с параметрами: сгенерить класс StripteaserAndDriver, взяв в качестве реализации НедоделанныйStripteaserAndDriver и добавить поддержку интерфейса IDriver и IStripteaser перенаправив вызовы IDriver-а в переменную driver, а вызовы IStripteaser в stripteaser.
Генератор соответственно делает следующих класс:
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-ы, но ссылки не вспомню.
свой, кривой

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

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

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

Потом, что так часто приходится с этим дело иметь. Если часто нужно наследоваться от, например, двух классов
Будут проблемы только, если имена у методов совпадаю. Таких проблем нет в C#.
Правда в Java удобнее перегрузать методы базовых классов с помошью анонимных классов.

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