Reputation: 123
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
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
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