I have a problem with the initialization of a List . The Class of the Items isn't known at compile time - they could be int, float, string or custom classes.
So I tried this:
public class Sensordevice {
private List<?> valueList;
public void setValueList(List<?> valueList) {
this.valueList = valueList;
}
public void addValue(Object value) {
if(valueList == null){
valueList = getList(value.getClass());
}
valueList.add(value);
}
private <T> List<T> getList(Class<T> requiredType) {
return new ArrayList<T>();
}
}
But I get this Error at valueList.add(value) in the addValue Methode:
The method add(capture#4-of ?) in the type List is not applicable for the arguments (Object)
Update
Thanks a lot for your replies. This solution works for my.
public class Sensordevice<T> {
private List<T> valueList;
public void setValueList(List<T> valueList) {
this.valueList = valueList;
}
public void addValue(T value) {
if(valueList == null){
valueList = new ArrayList<T>();
}
valueList.add(value);
}
}
This works for me. And by "works" I mean I don't get any errors. It doesn't seem to provide any functionality since there isn't any way to get the list of objects from the Sensordevice since getList just returns a new, empty list, but that's the code you gave. I think the core of the error is having addValue take Object instead of T.
public class Sensordevice {
private List valueList;
public <T> void setValueList(List<T> valueList) {
this.valueList = valueList;
}
public <T> void addValue(T value) {
if(valueList == null){
valueList = getList(value.getClass());
}
valueList.add(value);
}
private <T> List<T> getList(Class<T> requiredType) {
return new ArrayList<>();
}
}
public static void main(String[] args) {
Sensordevice sd = new Sensordevice();
sd.addValue(new Object());
sd.addValue(new Integer(3));
sd.addValue("");
sd.addValue(new Sensordevice());
System.out.println(sd.getList(Sensordevice.class));
}
So if you don't know particular type would you class use, make your class generic:
public class Sensordevice<T> {
private List<T> valueList;
public void setValueList(List<T> valueList) {
this.valueList = valueList;
}
public void addValue(T value) {
if(valueList == null){
valueList = getList(value.getClass());
}
valueList.add(value);
}
private List<T> getList() {
return new ArrayList<T>();
}
}
If you don't know the List type you can leave it without any type specification, just put: private List valueList;
Change the valueList to: private List valueList; and getList() to:
private <T> List<Object> getList(Class<T> requiredType) {
return new ArrayList<Object>();
}
This fixes the error and it appears to work properly.
I tested it with strings, floats, and ints.
Related
I'm trying to find a way to iterate through an enum's values while using generics. Not sure how to do this or if it is possible.
The following code illustrates what I want to do. Note that the code T.values() is not valid in the following code.
public class Filter<T> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
this.selectedOption = selectedOption;
for (T option : T.values()) { // INVALID CODE
availableOptions.add(option);
}
}
}
Here is how I would instantiate a Filter object:
Filter<TimePeriod> filter = new Filter<TimePeriod>(TimePeriod.ALL);
The enum is defined as follows:
public enum TimePeriod {
ALL("All"),
FUTURE("Future"),
NEXT7DAYS("Next 7 Days"),
NEXT14DAYS("Next 14 Days"),
NEXT30DAYS("Next 30 Days"),
PAST("Past"),
LAST7DAYS("Last 7 Days"),
LAST14DAYS("Last 14 Days"),
LAST30DAYS("Last 30 Days");
private final String name;
private TimePeriod(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
}
I realize it might not make sense to copy a enum's values to a list, but I'm using a library that needs a list of values as input and won't work with enums.
EDIT 2/5/2010:
Most of the answers proposed are very similar and suggest doing something like this:
class Filter<T extends Enum<T>> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
Class<T> clazz = (Class<T>) selectedOption.getClass();
for (T option : clazz.getEnumConstants()) {
availableOptions.add(option);
}
}
}
This would work great if I can be sure that selectedOption has a non-null value. Unfortunately, in my use case, this value is often null, as there is a public Filter() no-arg constructor as well. This means I can't do a selectedOption.getClass() without getting an NPE. This filter class manages a list of available options which of the options is selected. When nothing is selected, selectedOption is null.
The only thing I can think to solve this is to actually pass in a Class in the constructor. So something like this:
class Filter<T extends Enum<T>> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(Class<T> clazz) {
this(clazz,null);
}
public Filter(Class<T> clazz, T selectedOption) {
this.selectedOption = selectedOption;
for (T option : clazz.getEnumConstants()) {
availableOptions.add(option);
}
}
}
Any ideas how to do this without needing an extra Class parameter in the constructors?
This is a hard problem indeed. One of the things you need to do is tell java that you are using an enum. This is by stating that you extend the Enum class for your generics. However this class doesn't have the values() function. So you have to take the class for which you can get the values.
The following example should help you fix your problem:
public <T extends Enum<T>> void enumValues(Class<T> enumType) {
for (T c : enumType.getEnumConstants()) {
System.out.println(c.name());
}
}
Another option is to use EnumSet:
class PrintEnumConsants {
static <E extends Enum <E>> void foo(Class<E> elemType) {
for (E e : java.util.EnumSet.allOf(elemType)) {
System.out.println(e);
}
}
enum Color{RED,YELLOW,BLUE};
public static void main(String[] args) {
foo(Color.class);
}
}
For completeness, JDK8 gives us a relatively clean and more concise way of achieving this without the need to use the synthethic values() in Enum class:
Given a simple enum:
private enum TestEnum {
A,
B,
C
}
And a test client:
#Test
public void testAllValues() {
System.out.println(collectAllEnumValues(TestEnum.class));
}
This will print {A, B, C}:
public static <T extends Enum<T>> String collectAllEnumValues(Class<T> clazz) {
return EnumSet.allOf(clazz).stream()
.map(Enum::name)
.collect(Collectors.joining(", " , "\"{", "}\""));
}
Code can be trivially adapted to retrieve different elements or to collect in a different way.
Using an unsafe cast:
class Filter<T extends Enum<T>> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
Class<T> clazz = (Class<T>) selectedOption.getClass();
for (T option : clazz.getEnumConstants()) {
availableOptions.add(option);
}
}
}
If you are sure that selectedOption of the constructor Filter(T selectedOption) is not null. You can use reflection. Like this.
public class Filter<T> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
this.selectedOption = selectedOption;
for (T option : this.selectedOption.getClass().getEnumConstants()) { // INVALID CODE
availableOptions.add(option);
}
}
}
Hope this helps.
If you declare Filter as
public class Filter<T extends Iterable>
then
import java.util.Iterator;
public enum TimePeriod implements Iterable {
ALL("All"),
FUTURE("Future"),
NEXT7DAYS("Next 7 Days"),
NEXT14DAYS("Next 14 Days"),
NEXT30DAYS("Next 30 Days"),
PAST("Past"),
LAST7DAYS("Last 7 Days"),
LAST14DAYS("Last 14 Days"),
LAST30DAYS("Last 30 Days");
private final String name;
private TimePeriod(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
public Iterator<TimePeriod> iterator() {
return new Iterator<TimePeriod>() {
private int index;
#Override
public boolean hasNext() {
return index < LAST30DAYS.ordinal();
}
#Override
public TimePeriod next() {
switch(index++) {
case 0 : return ALL;
case 1 : return FUTURE;
case 2 : return NEXT7DAYS;
case 3 : return NEXT14DAYS;
case 4 : return NEXT30DAYS;
case 5 : return PAST;
case 6 : return LAST7DAYS;
case 7 : return LAST14DAYS;
case 8 : return LAST30DAYS;
default: throw new IllegalStateException();
}
}
#Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
And usage is quite easy:
public class Filter<T> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
this.selectedOption = selectedOption;
Iterator<TimePeriod> it = selectedOption.iterator();
while(it.hasNext()) {
availableOptions.add(it.next());
}
}
}
To get the value of the generic enumeration:
protected Set<String> enum2set(Class<? extends Enum<?>> e) {
Enum<?>[] enums = e.getEnumConstants();
String[] names = new String[enums.length];
for (int i = 0; i < enums.length; i++) {
names[i] = enums[i].toString();
}
return new HashSet<String>(Arrays.asList(names));
}
Note in the above method the call to the toString() method.
And then define the enumeration with such a toString() method.
public enum MyNameEnum {
MR("John"), MRS("Anna");
private String name;
private MyNameEnum(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}
I did it like this
protected List<String> enumToList(Class<? extends Enum<?>> e) {
Enum<?>[] enums = e.getEnumConstants();
return Arrays.asList(enums).stream()
.map(name -> name.toString())
.collect(Collectors.toList());
}
The root problem is that you need to convert an array to a list, right? You can do this, by using a specific type (TimePeriod instead of T), and the following code.
So use something like this:
List<TimePeriod> list = new ArrayList<TimePeriod>();
list.addAll(Arrays.asList(sizes));
Now you can pass list into any method that wants a list.
Here below an example of a wrapper class around an Enum.
Is a little bit weird bu is what i need :
public class W2UIEnum<T extends Enum<T> & Resumable> {
public String id;
public String caption;
public W2UIEnum(ApplicationContext appContext, T t) {
this.id = t.getResume();
this.caption = I18N.singleInstance.getI18nString(t.name(), "enum_"
+ t.getClass().getSimpleName().substring(0, 1).toLowerCase()
+ t.getClass().getSimpleName().substring(1,
t.getClass().getSimpleName().length()), appContext
.getLocale());
}
public static <T extends Enum<T> & Resumable> List<W2UIEnum<T>> values(
ApplicationContext appContext, Class<T> enumType) {
List<W2UIEnum<T>> statusList = new ArrayList<W2UIEnum<T>>();
for (T status : enumType.getEnumConstants()) {
statusList.add(new W2UIEnum(appContext, status));
}
return statusList;
}
}
The exception is in the ListCreator i 'when' method line with adding t
Do you guys can maybe tell me why I get this exception? Intelij doesnt see any mistakes in syntax and logic so whats the problem?
I wanted to make my own mapper and selector (filter) and i get this all the time. Is anything wrong here?
Can you please see what might cause this?
I need to use generics and I get lost with those.
package zad1;
import java.util.*;
public class ListCreator<T> {// Uwaga: klasa musi być sparametrtyzowana
List<T> list;
protected ListCreator(List<T> listcreate){
this.list = listcreate; //kreator robi nowa liste zeby nie modyfikowac
}
public static <T> ListCreator<T> collectFrom(List<T> srclist){
ListCreator<T> listCreator = new ListCreator<>(srclist);
return listCreator;
}
public ListCreator<T> when(Selector<T> selector){
List<T> nextlist = new ArrayList<>();
for (T t: this.list){
if (selector.select(t)){
this.list.add(t); //dla kazdego elementu list ktoremu kolejno odpowiada t, jesli spelnia warunek to element jest dodawany do nowej listy
}
}
this.list = nextlist;
return this;
}
public <S> List<S> mapEvery(Mapper<T,S> map){
List<S> dlist = new ArrayList<>();
for (T arg: this.list){
dlist.add(map.map(arg));
}
return dlist;
}
}
package zad1;
import java.util.*;
public class Main {
public Main() {
List<Integer> src1 = Arrays.asList(1,7,9,11,12);
System.out.println(test1(src1));
List<String> src2 = Arrays.asList("a", "zzzz", "vvvvvvv");
System.out.println(test2(src2));
}
public List<Integer> test1(List<Integer> src) {
Selector <Integer> sel = new Selector<Integer>() {
#Override
public boolean select(Integer arg) {
return arg < 10;
}
};
Mapper <Integer,Integer> map = new Mapper<Integer, Integer>() {
#Override
public Integer map(Integer arg) {
return arg + 10;
}
};
return ListCreator.collectFrom(src).when(sel).mapEvery(map);
}
public List<Integer> test2(List<String> src) {
Selector <String> sel = new Selector<String>() {
#Override
public boolean select(String arg) {
if (arg.length() > 3)
return true;
else
return false;
}
};
Mapper <String, Integer> map = new Mapper<String, Integer>() {
#Override
public Integer map(String arg) {
return arg.length() + 10;
}
};
return ListCreator.collectFrom(src).when(sel).mapEvery(map);
}
public static void main(String[] args) {
new Main();
}
}
Currently I'm writing a program where I have the following statement.
List<BaseballStatistic> q = BaseballStatistic.FIND.where().eq("teamID", "CHN").query();
Here, it complains
Unchecked assignment: 'java.util.List' to 'java.util.List'. Reason: 'BaseballStatistic.FIND.where().eq("teamID", "CHN")' has raw type, so result of query is erased more...
I have an interface which looks like this
public interface Query<T> {
...
List<T> execute();
}
then an abstract class that implements this interface
public abstract class AbstractQuery<T> implements Query<T> {
Statement _statement = null;
String _tableName;
List<Clause> _clauses;
Class<T> _type;
AbstractQuery(Class<T> type) {
_type = type;
_clauses = new ArrayList<>();
_tableName = type.getAnnotation(Table.class).name();
}
...
public abstract List<T> execute();
}
and finally a concrete implementation:
public class SimpleQuery<T> extends AbstractQuery<T> {
public SimpleQuery(Class<T> type) {
super(type);
}
which houses the following .query function which looks like:
#Override
public List<T> execute() {
try {
JSONObject jsonObject = Peanut.getClient().listStatistics(buildQuery());
if (jsonObject == null || !jsonObject.has("results")) {
return Collections.emptyList();
}
JSONArray columnNames = jsonObject.getJSONArray("columns");
Map<String, Integer> columnNameMap = new HashMap<>();
for (int i = 0; i < columnNames.length(); i++) {
columnNameMap.put((String) columnNames.get(i), i);
}
JSONArray results = jsonObject.getJSONArray("results");
List<T> ts = new ArrayList<>();
for (int i = 0; i < results.length(); i++) {
JSONArray result = results.getJSONArray(i);
T t = _type.newInstance();
for (Field field : ObjectUtils.getFieldsUpTo(t.getClass(), PinotModel.class)) {
if (field.getAnnotation(Column.class) == null) {
continue;
}
Object obj = ObjectUtils.getDefaultValue(field.getType());
String columnName = field.getAnnotation(Column.class).name();
if (columnNameMap.containsKey(columnName)) {
int idx = columnNameMap.get(columnName);
field.setAccessible(true);
field.set(t, ObjectUtils.convertObject(obj, result.get(idx)));
}
}
ts.add(t);
}
return ts;
} catch (Exception e) {
// TODO: Throw Peanut specific error.
Peanut.LOG.error(e);
return Collections.emptyList();
}
}
It seems like here, at compilation, the returned list has lost it's type leading to the warning. If I change the original variable declaration to List the warning will leave, which makes sense.
Is there anyway around this or is there a larger fundamental issue at play?
EDIT:
Query Function that calls execute is here
public List<T> query() {
return _query.execute();
}
And the relationship between SimpleQuery and BaseballStatistic.Find is as follows.
#Table(name = "baseballStats")
public class BaseballStatistic extends PinotModel {
public static final Find FIND = new Find<BaseballStatistic (BaseballStatistic.class) { };
...
and PinotModel looks like
public class PinotModel {
public static class Find<T> {
private final Class<T> type;
protected Find(Class<T> type) {
this.type = type;
}
public Query select(String... s) {
return new SimpleQuery<T>(type).select(s);
}
public Clause where() {
return new SimpleQuery<T>(type).where();
}
public Clause limit(Integer n) {
return new SimpleQuery<T>(type).limit(n);
}
public Clause top(Integer n) {
return new SimpleQuery<T>(type).top(n);
}
public Clause orderBy(String columnName, Order o) {
return new SimpleQuery<T>(type).orderBy(columnName, o);
}
public String tableName() {
return new SimpleQuery<T>(type).getTableName();
}
}
}
There are 2 places that you're missing generic type parameters.
BaseballStatistic.FIND:
public static final Find<BaseballStatistic> FIND = new Find<BaseballStatistic> (BaseballStatistic.class) { };
PinotModel.select:
public Query<T> select(String... s) {
return new SimpleQuery<T>(type).select(s);
}
You're also missing type parameters on PinotModel.where(). Clause would also need a type parameter, including on the AbstractQuery._clauses field.
I'm trying to find a way to iterate through an enum's values while using generics. Not sure how to do this or if it is possible.
The following code illustrates what I want to do. Note that the code T.values() is not valid in the following code.
public class Filter<T> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
this.selectedOption = selectedOption;
for (T option : T.values()) { // INVALID CODE
availableOptions.add(option);
}
}
}
Here is how I would instantiate a Filter object:
Filter<TimePeriod> filter = new Filter<TimePeriod>(TimePeriod.ALL);
The enum is defined as follows:
public enum TimePeriod {
ALL("All"),
FUTURE("Future"),
NEXT7DAYS("Next 7 Days"),
NEXT14DAYS("Next 14 Days"),
NEXT30DAYS("Next 30 Days"),
PAST("Past"),
LAST7DAYS("Last 7 Days"),
LAST14DAYS("Last 14 Days"),
LAST30DAYS("Last 30 Days");
private final String name;
private TimePeriod(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
}
I realize it might not make sense to copy a enum's values to a list, but I'm using a library that needs a list of values as input and won't work with enums.
EDIT 2/5/2010:
Most of the answers proposed are very similar and suggest doing something like this:
class Filter<T extends Enum<T>> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
Class<T> clazz = (Class<T>) selectedOption.getClass();
for (T option : clazz.getEnumConstants()) {
availableOptions.add(option);
}
}
}
This would work great if I can be sure that selectedOption has a non-null value. Unfortunately, in my use case, this value is often null, as there is a public Filter() no-arg constructor as well. This means I can't do a selectedOption.getClass() without getting an NPE. This filter class manages a list of available options which of the options is selected. When nothing is selected, selectedOption is null.
The only thing I can think to solve this is to actually pass in a Class in the constructor. So something like this:
class Filter<T extends Enum<T>> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(Class<T> clazz) {
this(clazz,null);
}
public Filter(Class<T> clazz, T selectedOption) {
this.selectedOption = selectedOption;
for (T option : clazz.getEnumConstants()) {
availableOptions.add(option);
}
}
}
Any ideas how to do this without needing an extra Class parameter in the constructors?
This is a hard problem indeed. One of the things you need to do is tell java that you are using an enum. This is by stating that you extend the Enum class for your generics. However this class doesn't have the values() function. So you have to take the class for which you can get the values.
The following example should help you fix your problem:
public <T extends Enum<T>> void enumValues(Class<T> enumType) {
for (T c : enumType.getEnumConstants()) {
System.out.println(c.name());
}
}
Another option is to use EnumSet:
class PrintEnumConsants {
static <E extends Enum <E>> void foo(Class<E> elemType) {
for (E e : java.util.EnumSet.allOf(elemType)) {
System.out.println(e);
}
}
enum Color{RED,YELLOW,BLUE};
public static void main(String[] args) {
foo(Color.class);
}
}
For completeness, JDK8 gives us a relatively clean and more concise way of achieving this without the need to use the synthethic values() in Enum class:
Given a simple enum:
private enum TestEnum {
A,
B,
C
}
And a test client:
#Test
public void testAllValues() {
System.out.println(collectAllEnumValues(TestEnum.class));
}
This will print {A, B, C}:
public static <T extends Enum<T>> String collectAllEnumValues(Class<T> clazz) {
return EnumSet.allOf(clazz).stream()
.map(Enum::name)
.collect(Collectors.joining(", " , "\"{", "}\""));
}
Code can be trivially adapted to retrieve different elements or to collect in a different way.
Using an unsafe cast:
class Filter<T extends Enum<T>> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
Class<T> clazz = (Class<T>) selectedOption.getClass();
for (T option : clazz.getEnumConstants()) {
availableOptions.add(option);
}
}
}
If you are sure that selectedOption of the constructor Filter(T selectedOption) is not null. You can use reflection. Like this.
public class Filter<T> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
this.selectedOption = selectedOption;
for (T option : this.selectedOption.getClass().getEnumConstants()) { // INVALID CODE
availableOptions.add(option);
}
}
}
Hope this helps.
If you declare Filter as
public class Filter<T extends Iterable>
then
import java.util.Iterator;
public enum TimePeriod implements Iterable {
ALL("All"),
FUTURE("Future"),
NEXT7DAYS("Next 7 Days"),
NEXT14DAYS("Next 14 Days"),
NEXT30DAYS("Next 30 Days"),
PAST("Past"),
LAST7DAYS("Last 7 Days"),
LAST14DAYS("Last 14 Days"),
LAST30DAYS("Last 30 Days");
private final String name;
private TimePeriod(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
public Iterator<TimePeriod> iterator() {
return new Iterator<TimePeriod>() {
private int index;
#Override
public boolean hasNext() {
return index < LAST30DAYS.ordinal();
}
#Override
public TimePeriod next() {
switch(index++) {
case 0 : return ALL;
case 1 : return FUTURE;
case 2 : return NEXT7DAYS;
case 3 : return NEXT14DAYS;
case 4 : return NEXT30DAYS;
case 5 : return PAST;
case 6 : return LAST7DAYS;
case 7 : return LAST14DAYS;
case 8 : return LAST30DAYS;
default: throw new IllegalStateException();
}
}
#Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
And usage is quite easy:
public class Filter<T> {
private List<T> availableOptions = new ArrayList<T>();
private T selectedOption;
public Filter(T selectedOption) {
this.selectedOption = selectedOption;
Iterator<TimePeriod> it = selectedOption.iterator();
while(it.hasNext()) {
availableOptions.add(it.next());
}
}
}
To get the value of the generic enumeration:
protected Set<String> enum2set(Class<? extends Enum<?>> e) {
Enum<?>[] enums = e.getEnumConstants();
String[] names = new String[enums.length];
for (int i = 0; i < enums.length; i++) {
names[i] = enums[i].toString();
}
return new HashSet<String>(Arrays.asList(names));
}
Note in the above method the call to the toString() method.
And then define the enumeration with such a toString() method.
public enum MyNameEnum {
MR("John"), MRS("Anna");
private String name;
private MyNameEnum(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}
I did it like this
protected List<String> enumToList(Class<? extends Enum<?>> e) {
Enum<?>[] enums = e.getEnumConstants();
return Arrays.asList(enums).stream()
.map(name -> name.toString())
.collect(Collectors.toList());
}
The root problem is that you need to convert an array to a list, right? You can do this, by using a specific type (TimePeriod instead of T), and the following code.
So use something like this:
List<TimePeriod> list = new ArrayList<TimePeriod>();
list.addAll(Arrays.asList(sizes));
Now you can pass list into any method that wants a list.
Here below an example of a wrapper class around an Enum.
Is a little bit weird bu is what i need :
public class W2UIEnum<T extends Enum<T> & Resumable> {
public String id;
public String caption;
public W2UIEnum(ApplicationContext appContext, T t) {
this.id = t.getResume();
this.caption = I18N.singleInstance.getI18nString(t.name(), "enum_"
+ t.getClass().getSimpleName().substring(0, 1).toLowerCase()
+ t.getClass().getSimpleName().substring(1,
t.getClass().getSimpleName().length()), appContext
.getLocale());
}
public static <T extends Enum<T> & Resumable> List<W2UIEnum<T>> values(
ApplicationContext appContext, Class<T> enumType) {
List<W2UIEnum<T>> statusList = new ArrayList<W2UIEnum<T>>();
for (T status : enumType.getEnumConstants()) {
statusList.add(new W2UIEnum(appContext, status));
}
return statusList;
}
}
Why does following code not throws exception?
import java.util.ArrayList;
import java.util.List;
#SuppressWarnings("unchecked")
public class MainRunner {
public static void main(String[] args) {
List<String> s = new ArrayList<String>() {
{
add("a");
add("1");
add("1");
}
};
// List<Integer> i = (List<Integer>) listConvertor(s, new Integer("1"));
List<Integer> i = (List<Integer>) listConvertor(s, Integer.class);
System.out.println(i);
}
#SuppressWarnings("unchecked")
public static <T, P> List<?> listConvertor(List<T> inputList, P outputClass) {
List<P> outputList = new ArrayList<P>(inputList.size());
for (T t : inputList) {
outputList.add((P) t); // shouldn't be classCastException here?
}
return outputList;
}
}
I want to return List<P> instead of List<?> . But when I write List<P> , it means List<Class<P>> . i.e. in above case , it means List<Class<Integer>> , but I want List<Integer> as return.
I want below code: (so that i don't have to cast again at when method returns)
List<Integer> i = listConvertor(s, Integer.class);
System.out.println(i);
}
#SuppressWarnings("unchecked")
public static <T, P> List<P> listConvertor(List<T> inputList, P outputClass) {
List<P> outputList = new ArrayList<P>(inputList.size());
for (T t : inputList) {
outputList.add((P) t); // shouldn't be classCastException here?
}
return outputList;
}
}
This should do the job with minimal fuss:
public static <T, P> List<P> listConvertor(List<T> inputList, Class<P> outputClass) {
List<P> outputList = new ArrayList<P>(inputList.size());
for (T t : inputList) {
if( !outputClass.isInstance(t) )
throw new ClassCastException("Faked CCException");
#SuppressWarnings("unchecked")
P p = (P) t;
outputList.add(p);
}
return outputList;
}
no cast on the caller side
exception if inappropriate types are in the source list.
public static <T, P> List<P> listConvertor(List<T> inputList, Class<P> outputClass) {
remember, you are passing a Class Object, not an Integer Object.
No, of course it does not throw an exception. Generics are for compile time checks, not run-time. At run time, all your lists are List<Object> and the cast is made implicitly by the JVM.
Edit : in your code,
for (T t : inputList) {
outputList.add((P) t); // shouldn't be classCastException here?
}
is actually compiled to
for (Object t : inputList) {
outputList.add((Object) t); // shouldn't be classCastException here? -- no
}
For example, with your code, if you do i.get(0).getClass() you will then get a ClassCastException as the item cannot be converted from String to Integer.class (note: the same would apply however you do it as you cannot implicitly cast a String to an Integer. Period.)
If this is really what you want to do, cast T to P (for example, cast strings to a numeric value), then I suggest you use another pattern. For example :
static interface ClassConverter<F,T> {
public T convert(F o);
}
static class StringToIntConverter implements ClassConverter<String,Integer> {
public Integer convert(String o) {
try {
return Integer.parseInt(o);
} catch (NumberFormatException e) {
return 0;
}
}
}
public static void main(String[] args) {
List<String> s = new ArrayList<String>() {
{
add("a");
add("1");
add("1");
}
};
// List<Integer> i = (List<Integer>) listConvertor(s, new Integer("1"));
List<Integer> i = (List<Integer>) listConvertor(s, new StringToIntConverter());
System.out.println(i);
System.out.println(i.get(0).getClass().getName());
}
public static <T, P> List<P> listConvertor(List<T> inputList, ClassConverter<T, P> c) {
List<P> outputList = new ArrayList<P>(inputList.size());
for (T t : inputList) {
outputList.add(c.convert(t)); // cast handled by the class method == safer
}
return outputList;
}
Than all you need to do is implement the ClassConverter interface to any types you wish to covert T to P and pass it to your listConverter method.
import java.util.ArrayList;
import java.util.List;
public class MainRunner {
public static void main(String[] args) {
List<String> s = new ArrayList<String>() {
{
add("a");
add("1");
add("1");
}
};
List<Integer> i = listConvertor(s, Integer.class);
System.out.println(i);
}
public static <T, P> List<P> listConvertor(List<T> inputList, Class<P> outputClass) {
List<P> outputList = new ArrayList<P>(inputList.size());
for (T t : inputList) {
outputList.add((P) t); // shouldn't be classCastException here?
}
return outputList;
}
}
Same as #A.H.'s answer, but much simplified using Class.cast(), and also got rid of the unnecessary T:
public static <P> List<P> listConvertor(List<?> inputList, Class<P> outputClass) {
List<P> outputList = new ArrayList<P>(inputList.size());
for (Object t : inputList) {
P p = outputClass.cast(t);
outputList.add(p);
}
return outputList;
}