ramp
ramp

Reputation: 1256

Generics - Invoking a parametrized constructor from another using this

I have 2 constructors for a class. A parametrized constructor and a non parametrized one. The problem is I am unable to invoke the parametrized one from the other using this(...). However it works when I try the same invocation from somewhere else (say a main method)

Here is the code

package com.test.generics;

    public class Key<T> {

        private T t;

        public Key(T t){
            this.t = t;
        }

        public T getKey(){
            return t;
        }
    }

And then

package com.test.generics;

    public class KeyValueObject<T> {
        private String value;
        private Key<T> key;

        public KeyValueObject(Key<T> k){
            key = k;
            value = "-1";
        }

        public KeyValueObject(String keyString){
            this(new Key<String>(keyString)); //Here is the problem, this says the Constructor KeyValueObject<T>(Key<String> k) is undefined
        }
        public static void main(String[] args) {
            Key<String> keyStr = new Key<String>("x");
            KeyValueObject<String> keyValObj = new KeyValueObject<String>(keyStr); //I try the same thing from main method and it works.
        }
    }

Why does the compiler say "Constructor KeyValueObject(Key k) is undefined". I do have a constructor KeyValueObject(Key k) defined.

Also in the main method, I am pretty much doing the same thing. If the overloaded constructor had worked, I could have used new KeyValueObject("x")

Upvotes: 1

Views: 62

Answers (4)

Dennis Sch&#252;rholz
Dennis Sch&#252;rholz

Reputation: 15

The type Key<String> does not necessarily match Key<T> so the error is valid. (an unsafe typecast would be possible thought)

consider implementing a factory method like this:

public static KeyValueObject<String> createKeyValueObject(final String keyString) {
    return new KeyValueObject<String>(new Key<String>(keyString));
}

or for better use of the generic possibilities:

public KeyValueObject(T k) {
    this(new Key<T>(k));
}

Upvotes: 1

Binkan Salaryman
Binkan Salaryman

Reputation: 3048

As I already wrote in the comment, you can do this:

public class Program {
    public static void main(String[] args) {
        KeyValueObject<String> $0 = new KeyValueObject<>(new Key<>("x")); //old school
        KeyValueObject<String> $1 = new KeyValueObject<>("x"); //boxing constructor
        KeyValueObject<String> $2 = KeyValueObject.make("x"); //factory method
    }
}

class Key<T> {
    T key;

    public Key(T key){
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}

class KeyValueObject<T> {
    //Solution 2: Create a factory method to handle the case `T` == `String`
    static KeyValueObject<String> make(String value) {
        return new KeyValueObject<>(value);
    }

    String value;
    Key<T> key;

    KeyValueObject(Key<T> key){
        this.key = key;
        this.value = "-1";
    }

    //Solution 1: Change `String` to `T`
    KeyValueObject(T key){
        this(new Key<>(key));
    }
}

Upvotes: 1

SME_Dev
SME_Dev

Reputation: 1890

The declared type for the KeyValueObject<T> attribute:

private Key<T> key;

is different from the type argument in your constructor call:

public KeyValueObject(String keyString){
    this(new Key<String>(keyString));
}

The constructor's generic type argument must match with the attribute's type:

public KeyValueObject(T t){
    this(new Key<T>(t));
}

Upvotes: 3

Rog&#233;rio
Rog&#233;rio

Reputation: 16380

The compiler error is justified, because the code is not type safe. To see why, suppose you wrote:

KeyValueObject<Integer> kv = new KeyValueObject("not an Integer");

The above would produce an attempt to invoke KeyValueObject<Integer>(Key<Integer>) with a Key<String> argument, which would obviously not be valid.

Upvotes: 1

Related Questions