17

If I have an abstract class like this:

public abstract class Item
{
    private Integer value;
    public Item()
    {
        value=new Integer(0);
    }
    public Item(Integer value)
    {
        this.value=new Integer();
    }
}

And some classes deriving from Item like this:

public class Pencil extends Item
{
    public Pencil()
    {
        super();
    }
    public Pencil(Integer value)
    {
        super(value);
    }
}

I have not understood why I can't call the constructor using a generic:

public class Box <T extends Item>
{
    T item;
    public Box()
    {
        item=new T(); // here I get the error
    }
}

I know that is possible to have a type which hasn't a constructor, but this case is impossible because Pencil has the constructor without parameters, and Item is abstract. But I get this error from eclipse: cannot instanciate the type T
I don't understand why, and how to avoid this?

Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
  • Consider the case when you have another class that extends `Item`, except this class has only one constructor that insists on at least one argument. – Louis Wasserman Feb 20 '12 at 18:36
  • possible duplicate of [How can I instantiate a generic type in Java?](http://stackoverflow.com/questions/6916346/how-can-i-instantiate-a-generic-type-in-java) – Lukas Eder Jul 22 '12 at 12:23
  • possible duplicate of [Create instance of generic type in Java?](http://stackoverflow.com/questions/75175/create-instance-of-generic-type-in-java) – A.H. Jul 23 '12 at 13:32
  • For all the peoples that need something like that i think the simplest solution is to make the below: https://stackoverflow.com/questions/1090458/instantiating-a-generic-class-in-java – Manolis P. Aug 03 '21 at 18:35

7 Answers7

14

This is because Java uses erasure to implement generics, see this:

To quote the relevant parts from the above Wikipedia article:

Generics are checked at compile-time for type-correctness. The generic type information is then removed in a process called type erasure.

As a result of type erasure, type parameters cannot be determined at run-time.

Consequently, instantiating a Java class of a parameterized type is impossible because instantiation requires a call to a constructor, which is unavailable if the type is unknown.

You can go around this by actually providing the class yourself. This is well explained here:

Community
  • 1
  • 1
icyrock.com
  • 27,952
  • 4
  • 66
  • 85
13

There is no way to use the Java type system to enforce that a class hierarchy has a uniform signature for the constructors of its subclasses.

Consider:

public class ColorPencil extends Pencil
{
    private Color color;

    public ColorPencil(Color color)
    {
        super();
        this.color=color;
    }   
}

This makes ColorPencil a valid T (it extends Item). However, there is no no-arg constructor for this type. Hence, T() is nonsensical.

To do what you want, you need to use reflection. You can't benefit from compile-time error checking.

gawi
  • 13,940
  • 7
  • 42
  • 78
1

You can place an abstract method so that the implementing class can determine how to construct the new object.

public abstract T constructT();

and instead of calling

T t = new T()

you would call

T t = constructT();

On your implementing call it would be created as:

new Box<Integer>() {
    public Integer constructT(){ return new Integer(); }
}
PointedEars
  • 14,752
  • 4
  • 34
  • 33
htafoya
  • 18,261
  • 11
  • 80
  • 104
0

It's impossible to use T to call a constructor because if it would be possible than after a compilation you would get the code like this:

public class Box{
    Object item;
    public Box(){
        item = new Object();
    }
}

So if you would use this code and pass some object than you expect that there is the constructor of some specific type is called, but instead you get the Object constructor.

0

You can actually require a constructor for a generic type. It's not perfect but have a look at this:

public interface Constructor<T> {
    T constructor() ;
} 

It's a general purpose functional interface to describe every noArgs constructor. The java.util.function.Supplier can also be used as it is equivalent.

public class Box <T extends Item>
{
    T item;
    public Box(Constructor<T> ctor)
    {
        item= ctor.constructor();
    }
}

This would look somewhat like this on the callsite:

Box<Pencil> box = new Box<>(Pencil::new);
RedCrafter LP
  • 174
  • 1
  • 8
0

Because at runtime the type of T is unknown.

pauljwilliams
  • 19,079
  • 3
  • 51
  • 79
0

T is an alias for the actual type your class will handle, for example if you instantiate Box<Item> then T is really just an alias for Item. As you declare T extends Item then you know that T will have at least the same interface as Item, so you can treat it like one.

I think what you really want to do is not instantiate the item field in Box, but instead implement a couple of methods to let you manipulate that field.

public class Box<T extends Item> {
    private T item;

    public T getItem() {
        return this.item;
    }

    public void setItem(T item) {
        return this.item = item;
    }
}