Home > Software engineering >  Unable to specialize method templatization using the subclassing approach
Unable to specialize method templatization using the subclassing approach

Time:06-13

public class CSVParser {  
  protected <T extends Message> void addMessageToResult(Message message, Descriptor descriptor, ImmutableList.Builder<T> messageListBuilder) {
    . . . 
  }

For DynamicMessage I wanted to have a different handling so I tried

public class CSVParserDynamicMessage extends CSVParser {
  protected<DynamicMessage> void addMessageToResult(Message message, Descriptor descriptor, ImmutableList.Builder<DynamicMessage> messageListBuilder) {
    messageListBuilder.add(DynamicMessage.parseFrom(descriptor, message.toByteString()));
}

But it gives the compilation error

'addMessageToResult(Message, Descriptor, Builder)' in 'com.google.cloud.verticals.telco.taap.dataflow.dataingestion.common.parsers.CSVParserDynamicMessage' clashes with 'addMessageToResult(Message, Descriptor, Builder)' in 'com.google.cloud.verticals.telco.taap.dataflow.dataingestion.common.parsers.CSVParser'; both methods have same erasure, yet neither overrides the other

I'm having to do the ugly method below (haven't yet attempted to run and see if it actually works):

@Override
  protected<T extends Message> void addMessageToResult(Message message, Descriptor descriptor, ImmutableList.Builder<T> messageListBuilder)
    throws InvalidProtocolBufferException {
    messageListBuilder.add((T) DynamicMessage.parseFrom(descriptor, message.toByteString()));
}

Note that my attempt of subclassing was inspired from Java generics (template) specialization possible (overriding template types with specific types) but the difference seems to be that in my case the template paramter T doesn't exist at class level but only at method level. I'm not able to understand why it doesn't work for my usecase.

Any ideas are much appreciated.

CodePudding user response:

Update

What means T

Generic type parameters like T, U, R - are simply placeholders for types, but not the actual types. By convention type parameters are denoted with a single letter but multi-character type parameter will also compile:

public <Something> void doIt(Something s) {}

Something will not be treated by the compiler as a class or interface name, it's just a placeholder.

protected <DynamicMessage> void addMessageToResult(Message message, Descriptor descriptor, ImmutableList.Builder<DynamicMessage> messageListBuilder) {}

As well as DynamicMessage is a placeholder like T. That means that you've changed the generic type parameter declared by in the super class as T extends Message to an arbitrary T. Which is incorrect.

At the beginning I didn't spotted your intention to override a generic method with a non-generic one, that would not work.

The reason for that is that T extends Message compiles to Object due to generic type erasure. I.e. you might think of it, as there's a non-generic method with an argument of Object type declared in the parent class, and therefore it's not possible to override it by providing DynamicMessage as type instead of Object.

A generic method can be overridden only by another generic method, which generic type parameters are precisely the same.


The problem is fully described in the error message: you can't have two method with the same name and arguments that differ only by a generic type parameter, due to generic type erasure.

For instance, these two methods will clash (i.e. their signatures will be treated as they are the same):

public void doIt(List<Integer> list) {}
public void doIt(List<String> list) {}

Because at runtime, there would be only a List<Object> and safe checkcasts added by the compiler.

The remedy when we need two distinct methods:

  • the method signatures should differ either by method-name, or by the set of parameters (generic type parameters would not be taken into account).
  • Related