3

I realize the title is kind of messy, but that's the best I could come up with.

Below is a minimal example of what I wish, but am currently failing to do.

public class ObjectA {}

public class ObjectB extends ObjectA {}

public interface HandlerInterface<T extends ObjectA> {

        public T easyToOverride();

        public List<T> hardToOverride();

}      

public class HandlerA implements HandlerInterface<ObjectA> {

        public ObjectA easyToOverride() {
                return new ObjectA();
        }

        public List<ObjectA> hardToOverride() {
                return new ArrayList<ObjectA>();
        }
}      

public class HandlerB extends HandlerA implements HandlerInterface<ObjectB> {

        /*
          This method ovverides its super method with ease since its return
          type directly inherits from the super class's return type
        */
        public ObjectB easyToOverride() {
                return new ObjectB();
        }

        /*
          This method is NOT accepted by the Java syntax since List<ObjectB>
          does NOT directly inherit from List<ObjectA>

          The method signature for hardToOverride() clashes with the signature
          in the super class and is not overridden because the return types
          don't obviously inherit each other
        */
         public List<ObjectB> hardToOverride() {
                return new ArrayList<ObjectB>();
        }
} 

Ignore that these classes should be in their own files and that I have not included their imports. I just put it like this to make it easier to read.

As you may have understood by the comments in the class HandlerB, the method hardToOverride() is not accepted (throw this code into your IDE and watch it scream).

I realise I could just return a List<Object> and type cast the contents of the returned List object to the type that I personally know the specific handler instance returns (ObjectA or ObjectB), but that would mean that anyone using these methods has to understand the inner workings of them, and I do not like that.

What I want is to be able to override the List<ObjectA> hardToOverride() method with a method List<ObjectB> hardToOverride() without losing the hard typing that these methods provide.

So my final question is:

Is there any way to keep all of these interfaces, inheritances and overrides without loosing the strong typing they provide in my example code?

If not, what is a better way to achieve a similar set of classes and interfaces that actually works?

Hannes Knutsson
  • 111
  • 1
  • 14
  • 1
    Some background -- [Why are arrays covariant but generics are invariant?](https://stackoverflow.com/questions/18666710/why-are-arrays-covariant-but-generics-are-invariant) – Andy Thomas Feb 14 '20 at 16:54
  • You can create a interface, CommonInterface, and put all the common methods from ObjectA and ObjectB and Then on the parent and child rewrite the method to return List. if you need to use methods exclusive to ObjectA then first check if a object is instanceof ObjectA and then cast it to use methods from ObjectA class. – printfmyname Feb 14 '20 at 17:22

1 Answers1

1

Your code is accepted if you declare HandlerA with a new generic even if this is never used really:

public class HandlerA<T> implements HandlerInterface<ObjectA> {
//....
}

NOTE: This is to be considered just a workaround but as result your example code will work as you asked. Moreover, even if HandlerA declares a generic, you can anycase instantiate it also without brackets:

ObjectA objectA = new HandlerA();
gregorycallea
  • 1,218
  • 1
  • 9
  • 28
  • Does that not mean that we could instantiate HandlerA with ANY non abstract class since HandlerA is still a template class (because of the )? The whole point of my question is to eliminate as much ambiguity as possible and have the typing as strong as possible while still keeping the characteristics I've attempted to describe. – Hannes Knutsson Feb 14 '20 at 21:03
  • Yes. You can also limit the usable classes using the **extends** syntax on generic but this doesn't eliminate the ambiguity unless you really need a new generic on HandlerA. As I said this is just a workaround to let you use exactly the code you shared. The disadvantage is to introduce a low ambiguity for the coder but you can solve this documenting the class specifically. – gregorycallea Feb 17 '20 at 10:45