andyInCambridge
andyInCambridge

Reputation: 1225

java generic extension type error

I'm writing a generic encoder/decoder and running into an issue with an extended generic. The idea is that I want to have an abstract Encodeable class that has a "virtual" static method decode which takes a Byte[] and constructs the object, similar to Serializable. (I know this can't really be done in java.) Each class that extends Encodeable will overwrite the encode/decode methods. I then want to use these subclasses of Encodeable generically. Here's an attempt to show what I mean:

public class Encodeable{

    // I'd like to write 
    // static abstract Encodeable decode(Byte[]);
    // similar to 
    // virtual Encodeable decode(Byte[]) = 0;  
    // in C++, but that seems to be illegal in java

    static Encodeable decode(Byte[] buf){return null};

}

public class EncodeableFoo extends Encodeable{

      static EncodeableFoo decode(Byte[] buf){
          // do actual decoding logic here
      }

}

public class Bar<T extends Encodeable>{

    public void messageReceived(MessageEvent e){
        Byte[] buf = e.getMessage();
        T messageObj = T.decode(buf);
        // do something with T
    }

}

As is, I get an error message like

error: incompatible types
    T messageObj = T.decode(objBuf);
                           ^
  required: T
  found:    Encodeable
  where T is a type-variable:
    T extends Encodeable declared in class EdgeClientHandler

at compile time. But if I change the decode line to

T messageObj = (T) T.decode(objBuf);

it works just fine. Can someone explain this black magic to me? Or, more importantly, give me a better way of writing my generic Bar class such that it knows T has a static method decode (and a non-static method encode)?

Upvotes: 0

Views: 167

Answers (2)

Matt Ball
Matt Ball

Reputation: 359826

First, static methods in Java cannot be abstract.

Second, you need to declare the method to be generic if you want the compiler to understand that you're returning a T, and not just an Encodeable. I recommend staying away from static entirely here. The basic idea:

public interface Decoder<T> {
    T decode(Byte[] buf);
}

public class FooDecoder implements Decoder<Foo> {
    Foo decode(Byte[] buf){
        // do actual decoding logic here
    }
}

public class Bar<T extends Encodeable> {

    private Decoder<T> decoder; // you'll have to figure out where to get this

    public void messageReceived(MessageEvent e){
        Byte[] buf = e.getMessage();
        T messageObj = decoder.decode(buf);
        // do something with T
    }
}

Your original setup seems to mix encodable types and things which know how to decode such types from a byte[], so I've renamed and fiddled a bit.


Side question: why Byte[]s instead of byte[]s?

Upvotes: 6

assylias
assylias

Reputation: 328608

Your design looks a little messy to me. I would not use a static method and do something like this instead:

public interface Decoder<T extends Encodable> {
    T decode(Byte[] buf);
}

public class Bar<T extends Encodable> {
    private final Decoder<T> decoder;

    public Bar(Decoder<T> decoder) {
        this.decoder = decoder;
    }

    public void messageReceived(MessageEvent e) {
        Byte[] buf = e.getMessage();
        T messageObj = decoder.decode(buf);
        // do something with T
    }
}

Upvotes: 3

Related Questions