Есть ли способ вернуть полиморфное это в Java?
В Typescript есть эта концепция полиморфного возвращаемого типа this
, https://www.typescriptlang.org/docs/handbook/advanced-types.html Пример:
export abstract class Animal {
private name: string;
public setName(name: string): this {
this.name = name;
return this;
}
}
export class Dog extends Animal {
private breed: string;
public setBreed(breed: string): this {
this.breed = breed;
return this;
}
}
export class FluffyDog extends Dog {
private fluffiness: number;
public setFluffiness(fluffiness: number): this {
this.fluffiness = fluffiness;
return this;
}
}
export class Main {
constructor() {
const dog: FluffyDog = new FluffyDog()
.setName('Fluffy')
.setFluffiness(10)
.setBreed('Lab');
}
}
Есть ли что-нибудь эквивалентное в Java? Лучшее, что я придумал, это:
public abstract class Animal<T extends Animal<T>> {
private String name;
public T setName(String name) {
this.name = name;
return (T)this;
}
}
class Dog extends Animal<Dog> {
private String breed;
public Dog setBreed(String breed) {
this.breed = breed;
return this;
}
}
class Main {
static {
Dog dog = new Dog()
.setName("Fluffy")
.setBreed("Lab");
}
}
Или это:
public abstract class Animal {
private String name;
public <T extends Animal> T setName(String name) {
this.name = name;
return (T)this;
}
}
class Dog extends Animal {
private String breed;
public <T extends Dog> T setBreed(String breed) {
this.breed = breed;
return (T)this;
}
}
class FluffyDog extends Dog {
private Long fluffiness;
public <T extends FluffyDog> T setFluffiness(Long fluffiness) {
this.fluffiness = fluffiness;
return (T)this;
}
}
class Main {
static {
FluffyDog dog = new FluffyDog()
.<FluffyDog>setName("Fluffy")
.setFluffiness(10L)
.setBreed("Lab");
}
}
Первый, кажется, может быть разделен только на один класс.
Второй требует явных аргументов типа в некоторых ситуациях.
Есть ли способ вернуть полиморфное это в Java?
2 ответа
Два других варианта:
Поскольку порядок методов установки не имеет значения, функционально вызывайте их в порядке снизу вверх. Приведение не требуется, если вы сначала назначаете переменную.
final FluffyDog dog = new FluffyDog(); dog.setFluffiness(10) .setBreed("Lab") .setName("Fluffy");
Переопределите унаследованные методы установки для изменения типа возвращаемого значения, используя
super
делегировать к фактической реализации:class Animal { private String name; public Animal setName(String name) { this.name = name; return this; } }
class Dog extends Animal { private String breed; @Override public Dog setName(String name) { super.setName(name); return this; } public Dog setBreed(String breed) { this.breed = breed; return this; } }
class FluffyDog extends Dog { private long fluffiness; @Override public FluffyDog setName(String name) { super.setName(name); return this; } @Override public FluffyDog setBreed(String breed) { super.setBreed(breed); return this; } public FluffyDog setFluffiness(long fluffiness) { this.fluffiness = fluffiness; return this; } }
class Main { public static void main(String[] args) throws Exception { final FluffyDog dog = new FluffyDog() .setName("Fluffy") .setBreed("Lab") .setFluffiness(10); } }
С вашей первой альтернативой вы все равно можете сделать два уровня наследования. По общему признанию это не очень читабельно и понятно. К сожалению, дженерики были дополнением к Java, добавленным в 1.5, и многие шаблоны Java Bean не очень хорошо подходят для такого рода цепочечных методов. (Вы заметите, что большинство методов установки возвращают void
в традиционных классах Java).
public abstract class Animal<T extends Animal<T>> {
private String name;
public T setName(String name) {
this.name = name;
return (T)this;
}
}
public class Dog<T extends Dog<T>> extends Animal<T> {
private String breed;
public T setBreed(String breed) {
this.breed = breed;
return (T) this;
}
}
public class FluffyDog extends Dog<FluffyDog> {
private Long fluffiness;
public <T extends FluffyDog> T setFluffiness(Long fluffiness) {
this.fluffiness = fluffiness;
return (T) this;
}
}
Если вы хотите разрешить FluffyDog
чтобы быть переопределенным и предлагать тот же шаблон, вам придется снова сделать то же самое с универсальным типом, как в Dog
учебный класс.