Re[2]: О множественном наследовании
От: maxkar  
Дата: 05.09.12 17:43
Оценка: 12 (2)
Здравствуйте, AndrewVK, Вы писали:

AVK>Мое предложение — множественное наследование интерфейсов и отсутствие наследования реализаций. Т.е. классы вообще не наследуются. А для реюза кода придумать способ упрощения агрегации.


Я тоже за эту идею. И хочу еще одну штуку, тесно связанную с отсутствием наследования реализаций. Я хочу избавиться от new ... и "конструкторов" как таковых. Методы "создания" объектов — это "обычные" с точки зрения всего остального кода статические методы.

Сама идея вполне естественно возникает в тех языках, где функция — объект первого класса. Периодически во всякие трансформаторы (вроде map и т.п.) хочется скормить конструктор объекта. А нельзя . Чисто теоретически, можно сделать синтаксическую конструкцию вида "map (new SomeClass) ... и это будет работать. Но останется еще одна проблема — перегрузка конструктора по имени и "неравноправие" конструкторов. Пример:
public class Rectangle {
  public Rectangle(int x, int y, int width, int height) {...}
  public static Rectangle createByPoints(int x1, int y1, int x2, int y2) { return new Rectangle(x1, y1, x2-x1, y2-y1); }
}

Проверки и т.п. можно добавить. createByPoints можно с двумя точками сделать, но я не хочу (для данного примера). Логичное решение здесь — сделать еще статический метод createByPointAndSize, а конструктор сделать приватным. Но все равно остается лишний код конструктора и обертка к нему. Немного, но писать все же лень.

Объясню, почему фича связана с наследованием реализаций. Классический "конструктор" в языках с наследованием реализаций на самом деле является инициализатором. Он не создает новый объект, а инициализирует существующий. Создать объект он не может потому, что не знает точную структуру объекта (может создаваться его наследник). Поэтому выделение объекта переложили на "пользователя" конструктора, и в коде создали оператор new. Синтаксически, наверное, можно было генерировать статические методы для создания объектов, но полностью от конструкторов отказаться не получилось бы (не ясно, где вызов родительского инициализатора, а где — создание нового объекта). Без наследования реализации любой "конструктор" теперь точно знает структуру объекта, который нужно создавать. Поэтому может и выделять память.

Как примерно это может выглядеть:
public class Rectangle {
  public constructor byPointAndSize(int x, int y, int width, int height) { this.x = x; this.y = y; ... }
  public static Rectangle byPoints(int x1, int y1, int x2, int y2) { return Rectange.byPointsAndSize(x1, y1, x2-x1, y2-y1); }
  public constructor square(int x, int y, int width) {
    initialize byPointAndSize(x, y, width, width);
  }
}

Конструктор (constructor) — это с точки зрения всего остального кода static <T> метод. T — класс, внутри которого объявлен конструктор. Все остальные модификаторы (доступ и т.п.) — как обычно. Внутри метода ведет себя почти как instance method (т.е. имеет this). Почти, потому что имеет специальную форму — вызов других инициализаторов ininialize(аналог существующих вызовов других конструкторов из конструктора). Соответственно, при генерации и выполнении сначала выделяется память и затем вызывается инициализатор (с уже существующим this). initialize — вызов другого инициализатора (без выделения памяти!). На вызов initialize можно навесить те же ограничения, которые навешиваются на вызов других конструкторов из конструктора (например, может быть только первым statement в конструкторе).

Итого. Вроде бы реализуемо. И получаются "first class constructors", которые являются обычными статическими методами. По синтаксису в простейших случаях можно сделать очень похожим на "стандартные" конструкторы, вместо new Point(3, 5) будет Point.new(3,5). Так что простые сценарии подобный подход не усложняет.