2

Is there any way of enforcing the subclass type on a parameter at compilation time?

Something like this but not for instances - for classes?

I have the classes:

public abstract class Animal {

  public abstract void follow(Animal a); // <-- how to declare it?

}

but I would like a subclass to never use the Animal base class, but rather only itself (the deriving class) as parameter:

public class Fish extends Animal {

  @Override
  public void follow(Fish f) { // error here, since it expects Animal
    tagAlong(f);
  }

  private void tagAlong(Fish f) {
    // do something  
  }

}

I want a Fish to only use parameters of type Fish, not Animal, the same way another subclass Parrot would use only a Parrot parameter on the method follow().

I would strongly prefer to enforce this at compilation time, but if nothing else is possible runtime is a (less desirable) option.

Rann Lifshitz
  • 4,040
  • 4
  • 22
  • 42
The Impaler
  • 45,731
  • 9
  • 39
  • 76
  • 1
    I think you should use generics mate – Rann Lifshitz Apr 16 '18 at 14:35
  • 3
    That's precisely what `` is for. – chrylis -cautiouslyoptimistic- Apr 16 '18 at 14:37
  • It seems that unfortunately Java doesn't provide a safe way of doing this. The subclass must explicitly state the parameter type as itself. A careless programmer could specify a different type when creating the subclass 'Fish extends Animal'. Java would compile it and run it, even if it doesn't make sense. – The Impaler Apr 16 '18 at 14:55
  • After much debate I decided NOT to implement the accepted solution. Doing so requires to change a massive amount of code: change all the subclasses in the tree (95 classes), and all references to any class on the tree from all other "client" classes (300+ classes). For example, every single "for" needed to be changed. Too much trouble for a basic benefit. Ideally this should be part of Java language itself, not a trick. – The Impaler Apr 16 '18 at 18:30
  • @TheImpaler just read your comment there - I think you're right: refactoring such a large amount of code just to add this doens't look like a great idea. Moreover a coding mistake (e.g. parametrizing `Fish` with `Duck`, etc.) would still defeat the purpose, while compiling just fine. I suspect there isn't a good solution to your issue unfortunately, given the context. – Mena Apr 16 '18 at 22:17

1 Answers1

4

There is a trick to this.

From a design perspective, it's ugly, but it'll work in your case.

First things first: note that there's nothing really wrong with parametrizing with Animal, and checking the type at runtime.

Here's how to enforce inheritance with same-type parameter as method's signature:

// made class generic
public abstract class Animal<T extends Animal<T>> {

    public abstract void follow(T a);

}

// parametrizes with own type
public class Fish extends Animal<Fish> {

    @Override
    public void follow(Fish a) {
        // compiles
    }

}

You could also declare a generic method follow instead, parametrized with <T extends Animal> (and just T in the signature), but you can't enforce Fish in the signature in the Fish class.

Mena
  • 47,782
  • 11
  • 87
  • 106
  • 1
    You can add `` for further type-checking – HBo Apr 16 '18 at 14:40
  • I think this question should be marked as a duplicate, rather then answering it yet again. A very good and complete answer for this can be found here : https://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current-type-with-a-type-variable – Rann Lifshitz Apr 16 '18 at 14:40
  • `public abstract class Animal>` makes more sense. – davidxxx Apr 16 '18 at 14:41
  • @RannLifshitz I don't see the one you point at as duplicate... am I missing something? – Mena Apr 16 '18 at 14:41
  • This seems to work. The compiler is complaining about it. Adding `>`. I think it should be `Fish extends Animal>>`. – The Impaler Apr 16 '18 at 14:41
  • @HBo and davidxxx good point, edited. – Mena Apr 16 '18 at 14:42
  • @Mena : https://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current-type-with-a-type-variable - the first answer here is a full blown version of your answer. – Rann Lifshitz Apr 16 '18 at 14:46
  • @RannLifshitz hmm. I see your point. The good answers there are the same as mine (or rather mine is the same as those). However the *question* seems to differ enough, and there is no accepted answer... Not sure what to do. – Mena Apr 16 '18 at 14:49
  • @Mena : I know what I am going to do (+1 for your answer). – Rann Lifshitz Apr 16 '18 at 14:50
  • 1
    @RannLifshitz - "the first answer" is not a fixed thing. It depends on whether the answers are sorted by activity, date, or votes. Better to say "the answer by Paul Bellora" (if that's what you meant). – Ted Hopp Apr 16 '18 at 14:51
  • @RannLifshitz thanks :) looks like there's consensus on closing as dupe anyway. – Mena Apr 16 '18 at 14:51
  • 1
    It seems that unfortunately Java doesn't provide a safe way of doing this. The subclass must explicitly state the parameter type as itself. A careless programmer could specify a different type when creating the subclass 'Fish extends Animal'. Java would compile it and run it, even if it doesn't make sense. – The Impaler Apr 16 '18 at 14:57
  • The bound here is useless. This declaration doesn't really accomplish anything more than `public abstract class Animal`does. – newacct May 05 '18 at 22:03