второй раз сталкиваюсь с таким в коде, второй раз не понимаю, зачем это двухэтажное извращение. никакого использования того, что класс сам себя параметризует, нет.
это бывает полезно в каких-то ситуациях? или это 100% pure perversion?
public class Parametrisation {
private class Provider<P extends Provider<P,C>, C extends Consumer<P,C>> {
}
private class Consumer<P extends Provider<P,C>, C extends Consumer<P,C>> {
P provider;
}
private class Seller extends Provider<Seller, Buyer> {
void sell() {
}
}
private class Buyer extends Consumer<Seller, Buyer> {
void buy() {
// похоже, чтоб не кастить здесь, сомнительное удовольствие ...
// no need to cast here
provider.sell();
}
}
}
Здравствуйте, mihhon, Вы писали: M>это бывает полезно в каких-то ситуациях? или это 100% pure perversion?
Распространенный прием во встроенных DSL. Используется в сочетании с ковариантным возвращаемым типом для return this без приведения.
Например:
interface Builder<T,B extends Builder<T,B>> {
B withSomething(Something s);
T build();
}
class FooBuilder implements Builder<Foo,FooBuilder> {
private Something s;
public FooBuilder withSomething(Something s) {
this.s = s;
return this;
}
public Foo build() {
return new Foo(s);
}
}
...
Foo foo = new FooBuilder().withSomething(s).build();
Re[2]: класс, параметризованый самим собой. зачем?
Здравствуйте, Baudolino, Вы писали:
B>Здравствуйте, mihhon, Вы писали: M>>это бывает полезно в каких-то ситуациях? или это 100% pure perversion? B>Распространенный прием во встроенных DSL. Используется в сочетании с ковариантным возвращаемым типом для return this без приведения. B>Например: B>
B>interface Builder<T,B extends Builder<T,B>> {
B> B withSomething(Something s);
B> T build();
B>}
B>class FooBuilder implements Builder<Foo,FooBuilder> {
B> private Something s;
B> public FooBuilder withSomething(Something s) {
B> this.s = s;
B> return this;
B> }
B> public Foo build() {
B> return new Foo(s);
B> }
B>}
B>...
B>Foo foo = new FooBuilder().withSomething(s).build();
B>
не вижу в примере необходимости в извращеиях,
то же самое, только проще и без самопараметризации,
нет под рукой компилятора, но должно компилироваться
interface Builder<T> {
T build();
}
class FooBuilder implements Builder<Foo> {
private Something s;
public FooBuilder(Something s) {
this.s = s;
}
public Foo build() {
return new Foo(s);
}
}
...
Foo foo = new FooBuilder(s).build();
If we dissect the declaration " Enum<E extends Enum<E>> " we can see that this pattern has several aspects.
First, there is the fact that the type parameter bound is the type itself: " Enum <E extends Enum <E>> ". It makes sure that only subtypes of type Enum are permitted as type arguments. (Theoretically, type Enum could be instantiated on itself, like in Enum<Enum> , but this is certainly not intended and it is hard to imagine a situation in which such an instantiation would be useful.)
Second, there is the fact that the type parameter bound is the parameterized type Enum <E> , which uses the type parameter E as the type argument of the bound. This declaration makes sure that the inheritance relationship between a subtype and an instantiation of Enum is of the form " X extends Enum<X> ". A subtype such as " X extends Enum<Y> " cannot be declared because the type argument Y would not be within bounds; only subtypes of Enum<X> are within bounds.
Third, there is the fact that Enum is generic in the first place. It means that some of the methods of class Enum take an argument or return a value of an unknown type (or otherwise depend on an unknown type). As we already know, this unknown type will later be a subtype X of Enum<X> . Hence, in the parameterized type Enum<X> , these methods involve the subtype X , and they are inherited into the subtype X . The compareTo method is an example of such a method; it is inherited from the superclass into each subclass and has a subclass specific signature in each case.
To sum it up, the declaration " Enum<E extends Enum<E>> " can be decyphered as: Enum is a generic type that can only be instantiated for its subtypes, and those subtypes will inherit some useful methods, some of which take subtype specific arguments (or otherwise depend on the subtype).
Re[3]: класс, параметризованый самим собой. зачем?
Здравствуйте, mihhon, Вы писали:
M>... M>не вижу в примере необходимости в извращеиях, M>то же самое, только проще и без самопараметризации, M>нет под рукой компилятора, но должно компилироваться M>...
билдеры на то и нужны, когда есть необходимость конструирования сложносоставных объектов (читай — методов withXXX очень много и помещать их все в конструктор нецелесообразно).
Вообще говоря, насколько мне известно, такой подход называется CRTP паттерном, несравненно более широкое применение он получил в С++ чем в Java/C#.
Re[4]: класс, параметризованый самим собой. зачем?
Здравствуйте, A13x, Вы писали:
A>Вообще говоря, насколько мне известно, такой подход называется CRTP паттерном, несравненно более широкое применение он получил в С++ чем в Java/C#.
CRTP — это немного другое, он позволяет статически из предка вызывать методы потомка. В С++ это возможно, так как определение потомка известно на момент развёртывания шаблона.
Sapienti sat!
Re[5]: класс, параметризованый самим собой. зачем?
Здравствуйте, Cyberax, Вы писали:
C>... C>CRTP — это немного другое, он позволяет статически из предка вызывать методы потомка. В С++ это возможно, так как определение потомка известно на момент развёртывания шаблона.
M>public class Clazz<T extends Clazz<T>> {
M> ...
M>}
M>
M>второй раз сталкиваюсь с таким в коде, второй раз не понимаю, зачем это двухэтажное извращение. никакого использования того, что класс сам себя параметризует, нет.
M>это бывает полезно в каких-то ситуациях? или это 100% pure perversion?
Самый, пожалуй, часто встречающийся случай
public class ComparableClass implements Comparable<ComparableClass> {
private final int sortOrder;
public ComparableClass(int sortOrder) {
this.sortOrder = sortOrder;
}
@Override
public int compareTo(ComparableClass o) {
return sortOrder - o.sortOrder;
}
}
Re[2]: класс, параметризованый самим собой. зачем?
Здравствуйте, Fuud, Вы писали:
F>Здравствуйте, mihhon, Вы писали:
M>>
M>>public class Clazz<T extends Clazz<T>> {
M>> ...
M>>}
M>>
M>>второй раз сталкиваюсь с таким в коде, второй раз не понимаю, зачем это двухэтажное извращение. никакого использования того, что класс сам себя параметризует, нет.
M>>это бывает полезно в каких-то ситуациях? или это 100% pure perversion?
F>Самый, пожалуй, часто встречающийся случай
F>
F>public class ComparableClass implements Comparable<ComparableClass> {
F> private final int sortOrder;
F> public ComparableClass(int sortOrder) {
F> this.sortOrder = sortOrder;
F> }
F> @Override
F> public int compareTo(ComparableClass o) {
F> return sortOrder - o.sortOrder;
F> }
F>}
F>
Comparable<T> is not parameterized by itself
public interface Comparable<T> {
public int compareTo(T o);
}
Re[5]: класс, параметризованый самим собой. зачем?
Здравствуйте, A13x, Вы писали:
A>билдеры на то и нужны, когда есть необходимость конструирования сложносоставных объектов (читай — методов withXXX очень много и помещать их все в конструктор нецелесообразно).
в примере конструируется сложносоставной Foo, и все параметры в итоге передаются в его конструктор )) тогда уж в самом Foo надо добавить withSomething1..132, а не вызывать его конструктор, если конструкторы не нравятся
Re[5]: класс, параметризованый самим собой. зачем?
Здравствуйте, mihhon, Вы писали:
M>Здравствуйте, A13x, Вы писали:
A>>билдеры на то и нужны, когда есть необходимость конструирования сложносоставных объектов (читай — методов withXXX очень много и помещать их все в конструктор нецелесообразно).
M>в примере конструируется сложносоставной Foo, и все параметры в итоге передаются в его конструктор )) тогда уж в самом Foo надо добавить withSomething1..132, а не вызывать его конструктор, если конструкторы не нравятся
Иногда все же удобнее применять Builder'ы:
1. Особенно для effectively immutable объектов (использую этот подход во всех java проектах, если это возможно).
2. Когда нужен явный шаг конструирования объекта, в котором проводится валидация всех параметров, что как раз удобнее всего сделать в конструкторе.
Re[6]: класс, параметризованый самим собой. зачем?
Возможно, просто код не будет компилироваться, насколько я помню со стандартной ошибкой об отсутствующем объявлении функции.
Вообще, шаблоны в С++ очень сильно отличаются от Generic в Java/C#, по сути в compile time можно сделать очень много всего, типа вычисления факториала. Из более жизненных сценариев использования — выбор стратегии работы с типом, к примеру если в коллекции используется примитивный тип — использовать массив для внутреннего представления элементов, если указатель на объект — включить проверки на NULL и вызывать деструктор при удалении объекта и т.д. и т.п.
Если для общего развития интересно взглянуть на возможности — советую посмотреть на описание библиотек из boost.org
Re[2]: класс, параметризованый самим собой. зачем?