erwingun2010
erwingun2010

Reputation: 123

How to cast implicit or automatically in java

I have some trouble with casting attributes "automatically".

public abstract class MyType<T> {
    public abstract T getValue(String content);
}

public class MyString extends MyType<String> {
    @Override
    public String getValue(String content) {
        return String.valueOf(content);
    }
}

public class MyInteger extends MyType<Integer> {
    @Override
    public Integer getValue(String content) {
        return Integer.valueOf(content);
    }
}

public class User {
    private String _name;
    private int _id;

    public void setName(String name) {
        _name = name;
    }

    public String getName() {
        return _name;
    }

    public void setId(int id) {
        _id = id;
    }

    public int getId() {
        return _id;
    }
}

public class MainTest {
    public static void main(String[] args) {
        ArrayList<MyType> myTypes = new ArrayList<MyType>();
        myTypes.add(new MyString());
        myTypes.add(new MyInteger());

        User user = new User();

        for (MyType myType : myTypes) {
            try {
                user.setName((String) myType.getValue("foobar")); // getValue always returns an Object that I have to parse
                user.setId((Integer) myType.getValue("42")); // getValue always returns an Object that I have to parse
            } catch (Exception e) {
            }
        }
    }
}

Please keep in mind that this is only an abstract example of my problem.

How can I replace the castings? I need something like this:

user.setName((user.getName().getClass()) myType.getValue("foobar"));

Unfortunately eclipse tells me that this is wrong The method getValue(String) is undefined for the type Class<capture#3-of ? extends String>

I will not cast explicity. I will cast more implicity/automatically.

Upvotes: 0

Views: 129

Answers (2)

Matt Ball
Matt Ball

Reputation: 359786

The problem is that the myTypes list is declared as a List<MyType>, which uses the raw type MyType instead of a particular parameterization of MyType in the list. This is generally discouraged, warned against, bad practice, etc.

Ordinarily you'd declare the list like List<MyType<String>> or List<MyType<Integer>>. That would enable you to get elements out without having to cast. However, this is problematic because you want to be able to mix MyType<String> and MyType<Integer> in the same list. Again, this is generally not recommended, and is a code smell.

So you could declare a List<MyType<?>> instead, which is better than List<MyType>, but not by much. The only correct conclusion is that the design is inherently flawed: don't try to mix heterogeneous types in collections.*


The class MyType was just an abstract example. Actually my question was how I can cast: user.setName((user.getName().getClass()) object); if object is Object object = "foobar";

Regardless, the problem is still fundamentally that you're using raw types in the list. It seems like you might be better off using something that maintains the type parameterization, such as a Map<Class<T>, MyType<T>> instead of a List<MyType>.

Consider applying the strategy pattern here.


*Expert mode: unless there is a sensible common supertype which you can use as an upper bound.

Upvotes: 9

Ryan Stewart
Ryan Stewart

Reputation: 128809

I agree 100% with Matt Ball's answer. A simple, quick solution, though maybe not the ideal one, is to remove the abstractness from MyType and add the ability to get the value as a specific type. You'll find this kind of pattern in ResultSet in the standard library, among other places:

public class MyType {
    private String content;
    MyType(String content) { this.content = content }
    public String getStringValue() { return content; }
    public Integer getIntValue() { return Integer.parseInt(content); }
}

Upvotes: 3

Related Questions