0

Imagine code like this:

public class Value<T> {
    private T value;

    public Value(T value) {
        this.value = value;
    }

    public int size() {
        return sizeOf(value);
    }

    private int sizeOf(int value) {
        return Integer.BYTES;
    }

    private int sizeOf(String value) {
        return value.length() * Character.BYTES;
    }
}

and somewhere (e.g. main)

Value<String> value = new Value<String>("hello");

The problem is that the code will not compile, because I haven't implemented sizeOf(T value), but I don't understand why would the compiler complain --- it sees that I only use it with <T=String>. And if I implement the generic sizeOf(T value) , it will take precedence (as explained here Java generics (template) specialization possible (overriding template types with specific types) )

Note that the solution proposed there (having a StringValue subclass) doesn't really apply in my case, because I already have some subclasses which I am using, so I would need to create lots of extra classes (times by types)

enter image description here

But I would prefer something much more straight-forward, such as this

enter image description here

Peter Uhnak
  • 9,617
  • 5
  • 38
  • 51
  • 1
    "but I don't understand why would the compiler complain --- it sees that I only use it with ``" - This is not how generics work in Java. They are quite different from C++'s template variables. You need a methode `sizeOf` that satisfies the errasure given by `T`. – Turing85 Oct 29 '16 at 10:25

2 Answers2

2

You have to declare sizeOf(T value) because T can be of any type, so to maintain type controll, compiler needs "universal" version of sizeOf method that will hindle any possible type.

Think of what would happend if you would use something like new Value<List<Object>>(new ArrayList<Object>).size() - what would happen then as you have no method specialized in returning size of collection?

The thing is, that during runtime, generic type is erased, so JVM have no idea whitch of the overloaded methods should be used. Check more info about type erasure here. https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
2

There's no getting around having to provide an implementation for size(). Your code is half way to the double dispatch pattern, but going all the way isn't possible, because you've got no way of requiring types like String to implement size().

One approach is to require the size code on construction:

public class Value<T> {
    private T value;
    private Function<T, Integer> size;

    public Value(T value, Function<T, Integer> size) {
        this.value = value;
        this.size;
    }

    public int size() {
        return size.apply(value);
    }
}

To create a Value<String> for example:

 Value<String> val = new Value("foo", s -> s.length() * Character.BYTES)

or if that doesn't work, use instanceof for supported types:

public int size() {
    if (value instanceof Integer)
        return Integer.BYTES;
    if (value instanceof String)
        return ((String)value).length() * Character.BYTES;
    // other supported types
    throw new IllegalStateException();
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722