Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java -- How to deal with type erasure in constructors?

Let's say I have two constructors in my class:

public User (List<Source1> source){
...
}

public User (List<Source2> source) {
...
}

Let's say that both of these constructors provide the same information about a User and are equally valid ways to construct a user for different use cases.

In Java, you can't do this because of type erasure -- Java won't accept two constructors that have as parameters List< ? >.

So, what is the way to get around this? What is a solution that is not overkill but still respects basic OO? It seems wrong to have to construct a factory method or other interface around this just because Java doesn't have strong generics support.

Here are the possibilities I can think of:

1) Accept a List<?> as a parameter for the constructor and parse in the constructor which kind of logic you need, or throw an exception if it's not any of the accepted types.

2) Create a class that accepts either List, constructs the appropriate User object, and returns it.

3) Create wrappers around List<Source1> and List<Source2> that can be passed to the User constructor instead.

4) Subclass this guy with two classes, where all of the functionality is inherited except for the constructor. The constructor of one accepts Source1, the other accepts Source2.

5) Wrap this guy with a builder where are two different builder methods for the two different sources of data for instantiation.

My questions are these:

1) Is the need to do this a flaw with Java, or an intentional design decision? What is the intuition?

2) Which solution is strongest in terms of maintaining good code without introducing unneeded complexity? Why?

This question is similar: Designing constructors around type erasure in Java but does not go into specifics, it just suggests various work-arounds.

like image 920
Jeremy Avatar asked Jun 07 '12 18:06

Jeremy


People also ask

What are the two 2 types of Erasure?

- Erasure is a type of alteration in document. It can be classified as chemical erasure and physical erasure.

How does type erasure works in Java?

Java For Testers Type erasure is a process in which compiler replaces a generic parameter with actual class or bridge method. In type erasure, compiler ensures that no extra classes are created and there is no runtime overhead.

What is the following class converted to after type erasure?

What is the following method converted to after type erasure? public static <T extends Comparable<T>> int findFirstGreaterThan(T[] at, T elem) { // ... } Answer: public static int findFirstGreaterThan(Comparable[] at, Comparable elem) { // ... }

How do you stop a constructor in Java?

The only way to "stop" a constructor is to throw an exception. Bearing in mind of course that the caller is supposed to "know" about this exception and be able to handle the case where the constructor fails.

Why do we need type erasure in Java?

To support generic programming, as well as perform a stricter type check, Java implements type erasure. All type parameters in generic types are replaced with the bound (if unbounded) or object type. This way, the bytecode will only contain classes, methods, and interfaces. Type casts to preserve the type.

What is type erasure in C++?

Type erasure can be explained as the process of enforcing type constraints only at compile time and discarding the element type information at runtime. The compiler replaces the unbound type E with an actual type of Object: Therefore the compiler ensures type safety of our code and prevents runtime errors. 3. Types of Type Erasure

How do you erase a type in Java?

Type Erasure in Java 1 All type parameters in generic types are replaced with the bound (if unbounded) or object type. This way, the bytecode will only contain classes, methods, and interfaces. 2 Type casts to preserve the type. 3 Bridge methods are generated so as to preserve the polymorphism concept in extended generic types.

How does the Java compiler preserve polymorphism of generic types after erasure?

In our example above, the Java compiler preserves polymorphism of generic types after erasure by ensuring no method signature mismatch between IntegerStack ‘s push (Integer) method and Stack ‘s push (Object) method. Consequently, Stack class's push method after type erasure, delegates to the original push method of IntegerStack class.


2 Answers

The usual approach is to use factory methods:

public static User createFromSource1(List<Source1> source) {
    User user = new User();
    // build your User object knowing you have Source1 data
    return user;
}

public static User createFromSource2(List<Source2> source) {
    User user = new User();
    // build your User object knowing you have Source2 data
    return user;
}

If you only want construction using Source1 or Source2 (ie you don't have a default constructor), you simply hide your constructor, forcing clients to use your factory methods:

private User () {
    // Hide the constructor
}

This problem arises because you can't name constructors differently, which would be how you'd overcome this if these were normal methods. Because constructor names are fixed as the class name, this code pattern is only way to distinguish then give the same type erasure.

like image 54
Bohemian Avatar answered Oct 01 '22 12:10

Bohemian


1: Maintaining backward compatibility with erasure.

2: Can your class use generics? Something like this:

public class User<T> {
    private List<T> whatever;
    public User(List<T> source){
       ....
    }
}

I am not sure if this is what you meant by (2)

like image 41
Rohit Avatar answered Oct 01 '22 13:10

Rohit