tags:

views:

173

answers:

3

I am porting some C# code over to Java. I am having trouble with the where Syntax, specifically new(). I understand that where is similar to Java's generic: T extends FOO.

How I can replicate the new() argument in Java?

"The new() Constraint lets the compiler know that any type argument supplied must have an accessible parameterless--or default-- constructor." - MSDN

ie:

public class BAR<T> : BAR
       where T : FOO, new()

Here is how I implemented cletus's solution:

public class BAR<T extends FOO> extends ABSTRACTBAR {
    public BAR(T t) throws InstantiationException, IllegalAccessException{ 
        t.getClass().newInstance();
        this.value = t;
    }
}
+5  A: 

You can't replicate that in Java because generics are fundamentally different between C# and Java. Java uses type erasure so generic type arguments aren't (mostly) retained at runtime. If you want to construct elements of your generic type argument then you'll need to pass in a class instance:

public class Bar<T extends Foo> {
  private final Class<T> clazz;

  public class Bar(Class<T> clazz) {
    this.clazz = clazz;
  }

  public T newInstance() {
    return clazz.newInstance(); // this will throw checked exceptions
  }
}

Edit: Just to cover the issue of runtime type safety of generic type arguments: clearly Java doesn't natively have it because of type erasure: there are no runtime types for generic type arguments. There is a solution however. You use Collections.checkedList():

List<String> list = Collections.checkedList(new ArrayList<String>(),
                      String.class);

This collection will now throw an exception if you try to insert something that isn't a String.

cletus
+1 I'll accept when it times out.
Shiftbit
I certainly didn't downvote, but while this is the best you can do in Java with generics, there is no runtime type safety. Nothing stops the type being passed in from being an interface itself or lacking a public default constructor.
Yishai
+1  A: 

You cannot. Java implements generic type safety with erasure and does not support the typing based on the signature of a constructor, so there is no way to do exactly that and have a static type check.

Yishai
+1  A: 

Hope this helps. Please note, this requires a no-arg constructor be present in the you are creating a new instance of.


public class TypeStuff {

    private static final long serialVersionUID = 1L;

    // *** PUBLIC STATIC VOID MAIN ***
    public static void main(String[] args) {
        TypeStuff ts = new TypeStuff();
        ts.run();
    }

    // *** CONSTRUCTOR ***
    public TypeStuff () {
    }

    public void run() {
        Fruit banana = new Banana();

        Fruit dupe = newT(banana);
        System.out.println("dupe.getColor()=" + dupe.getColor());

        Fruit orange = new Orange();
        Fruit dupe2 = newT(orange);
        System.out.println("dupe2.getColor()=" + dupe2.getColor());
    }

    public <T extends Fruit> T newT(T fruit) {
        T dupe = null;
        try {
            Class clazz = fruit.getClass();
            dupe = (T) clazz.newInstance();
        } catch (Exception ex) {
        } finally {
            return dupe;
        }
    }

    interface Fruit {
        String getColor();
    }
    static class Banana implements Fruit {
        public Banana() {
        }
        @Override
        public String getColor() {
            return "Yellow";
        }
    }
    static class Orange implements Fruit {
        public Orange() {
        }
        @Override
        public String getColor() {
            return "Orange";
        }
    }


} // CLOSE CLASS.


orange80