In Typescript, there is this concept of a polymorphic return type this.
https://www.typescriptlang.org/docs/handbook/advanced-types.html#polymorphic-this-types
Example:
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');
}
}
Is there anything equivalent in Java? The best I have come up with is:
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");
}
}
OR this:
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");
}
}
The first seems to be only able to be subclassed once.
The second requires explicit type arguments in some situations.
Is there any way to return polymorphic this in Java?
Two other options:
Since order of setter methods don't matter, functionally, call them in bottom-up order. No casting needed, if you assign to variable first.
final FluffyDog dog = new FluffyDog();
dog.setFluffiness(10)
.setBreed("Lab")
.setName("Fluffy");
Override inherited setter methods to change return type, using super to delegate to actual implementation:
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);
}
}
With your first alternative you can still make two levels of inheritance. Admittedly its not very readable and understandable. Unfortunately generics were a bolt-on on Java added in 1.5, and a lot of the Java Bean patterns do not lend themselves that well to these kinds of chained methods. (You will notice that most setter methods return void in the traditional Java classes).
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;
}
}
If you wanted to allow FluffyDog to be overridden and offer the same pattern then you will have to do the same thing again with the generic type like in the Dog class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With