Ran into an issue with generics and array types that I am unable to solve. It boils down to this. In the following code, how can I convert a generic List into an Array of the same generic type, while using a factory method ("T convert(String value)") to convert each individual element of the input generic List:
#Test
public void test(){
List<String> integers = Arrays.asList("1", "2", "3", "4");
Integer[] integerArray = new TypeConverter<Integer[]>(Integer[].class).convert(integers);
assertEquals(4, integerArray.length);
assertEquals(1, integerArray[0].intValue());
assertEquals(2, integerArray[1].intValue());
assertEquals(3, integerArray[2].intValue());
assertEquals(4, integerArray[3].intValue());
}
public class TypeConverter<T>{
Class<T> type;
public TypeConverter(Class<T> type) {
this.type = type;
}
T convert(List<String> values){
List output = new ArrayList();
for (String value : values) {
//have to use Object.class here since I cant get the non-array type of T:
output.add(new TypeConverter(type.getComponentType()).convert(value));
}
return (T) output.toArray();
}
T convert(String value){
//convert to an int;
if(type == Integer.class){
return (T) new Integer(Integer.parseInt(value));
}
return null;
}
}
As you can see, my naive approach was to simply use the toArray Method, and cast the result like so:
(T) value.toArray()
but this results in a ClassCastException:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer
Is there a way to solve this that I am not seeing or should I take another approach?
Edit
Here's the concrete code that I am trying to fix. Specifically the visitArray() method:
https://github.com/johncarl81/transfuse/blob/master/transfuse/src/main/java/org/androidtransfuse/analysis/adapter/AnnotationTypeValueConverterVisitor.java
You may use the alternate version of List.toArray that takes as a parameter the type of the array you want to get.
toArray Method
You may create an empty array with some method of the Array class.
Array.newInstance
So having the expected type you just use Array.newInstance(type, 0); and pass in the result to the toArray method.
Edit:
Since your generic type is an array, you need to get the type of the components, try this:
Object[] array = (Object[]) Array.newInstance(type.getComponentType(), 0);
return (T) output.toArray(array);
Your value conversion method has a little something I'll let you figure out how to solve :)
Cheers!
Don't try to cast to T, try casting to T[], as you are handling an array of T's.
I suggest ditching the reflection and reference arrays.
Slightly abusing inheritance:
public abstract class StringConverter<T> {
public List<T> convert(List<String> values) {
List<T> output = new ArrayList<>();
for (String value : values) {
output.add(convert(value));
}
return output;
}
public abstract T convert(String value);
}
public static StringConverter<Integer> toInteger() {
return new StringConverter<>() {
public Integer convert(String value) {
return Integer.parseInt(value);
}
};
}
This works for me:
import java.util.*;
import java.lang.reflect.*;
public class Foo {
public static void main(String[] args) {
new Foo().test();
}
public void test(){
List<String> integers = Arrays.asList("1", "2", "3", "4");
Integer[] integerArray = new TypeConverter<Integer>(Integer.class).convert(integers);
System.out.println(Arrays.deepToString(integerArray));
}
public class TypeConverter<T>{
Class<T> type;
public TypeConverter(Class<T> type) {
this.type = type;
}
T[] convert(List<String> values){
List<T> output = new ArrayList<>();
for (String value : values) {
output.add(convert(value));
}
return output.toArray((T[]) Array.newInstance(type, output.size()));
}
T convert(String value){
if(type == Integer.class){
return (T) new Integer(Integer.parseInt(value));
}
else if(type == Long.class){
return (T) new Long(Long.parseLong(value));
}
return null;
}
}
}
return (T) values.toArray(); should be return (T)output.toArray( new Integer[0])
no, you have to hard code new Integer[0]
Related
When I call printArray in my pvsm, the error I keep receiving is:
Exception in thread "main" java.lang.ClassCastException:
java.base/[Ljava.lang.Object; cannot be cast to java.base/[Ljava.lang.Integer
I know the problem is with the R[] result = (R[]) list.toArray(). I have no idea how to convert the ArrayList to an array and cast it to a generic at the same time. Note I cannot change the parameters of the function map or add any new functions.
public class Homework2 {
public static void main(String[] args){
Function<Integer,Integer> function = new CalculateSuccessor();
Double[] d= {2.0,4.0,8.0};
Integer[] i= {2,4,8};
printArray(map(function,i));
}
#SuppressWarnings("unchecked")
public static <R,D> R[] map(Function<R,D> function, D[] array){
ArrayList<R> list = new ArrayList<>();
for (D element: array){
list.add(function.apply(element));
}
// THIS LINE OF DAMN CODE
R[] result = (R[]) list.toArray();
return result;
}
public static <R> void printArray(R[] array){
System.out.print("{ ");
for (R element: array){
System.out.print(element + ", ");
}
System.out.print("}");
}
public static class CalculateSuccessor implements Function<Integer,Integer> {
#Override
public Integer apply(Integer parameter) {
return parameter * 2;
}
} //End CalcSuc
} //End Homework2
In another class I have
public interface Function<R,D> {
public R apply(D parameter);
}
which you need for the function.apply. My professor insisted we use this instead of importing Function.
Part one, you need the Class<R> in order to dynamically create an array R[]. I would prefer Arrays.toString over implementing my own version of that. I also needed a Function<D, R> (not a Function<R, D>). But making those changes like
public static void main(String[] args) {
Function<Integer, Integer> function = new CalculateSuccessor();
Double[] d = { 2.0, 4.0, 8.0 };
Integer[] i = { 2, 4, 8 };
System.out.println(Arrays.toString(map(Integer.class, function, i)));
}
public static <R, D> R[] map(Class<R> cls, Function<D, R> function, D[] array) {
ArrayList<R> list = new ArrayList<>();
for (D element : array) {
list.add(function.apply(element));
}
return list.toArray((R[]) Array.newInstance(cls, list.size()));
}
I get
[4, 8, 16]
You can extract the type information from the Function<D,R> because you implemented it with an actual class. So together with #Elliott Frisch answer.
public static <R, D> R[] map(Function<D, R> function, D[] array) {
ArrayList<R> list = new ArrayList<>();
for (D element : array) {
list.add(function.apply(element));
}
Class<?> componentClass = extractReturnType(function)
return list.toArray((R[]) Array.newInstance(componentClass, list.size()));
}
private static Class<?> extractReturnType(Function<?, ?> function) {
Type[] interfaces = function.getClass().getGenericInterfaces();
for(Type iface:interfaces) {
if (iface instanceof ParameterizedType && Function.class.equals(((ParameterizedType) iface).getRawType())) {
return (Class<?>) ((ParameterizedType) iface).getActualTypeArguments()[1];
}
}
throw new IllegalArgumentException("Unable to extract type information");
}
How to make this method generic to return a List of any type currently it returns a List of String:
public static List<String> splitCommaSeparatedStringToListAnyDataTypeArray(String s) {
List<String> ints = new ArrayList<>();
if (isNull(s)) {
return ints;
}
String[] split = s.split(COMMA);
for (String st : split) {
ints.add(String.valueOf(st));
}
return ints;
}
Assuming that you use Java 8, you could use a Function to convert a String to a given type, you would then rewrite your method as next:
public static <T> List<T> splitCommaSeparatedStringToList(String s,
Function<String, T> function) {
if (isNull(s)) {
return Collections.emptyList();
}
// Convert the array of String into a List of T
return Arrays.stream(s.split(COMMA)).map(function).collect(Collectors.toList());
}
Your initial method would be the equivalent of splitCommaSeparatedStringToList(myInputString, String::valueOf).
For previous version of Java, the logic is the same, simply use FluentIterable from Google Guava to replace the Stream and use com.google.common.base.Function instead of java.util.function.Function as mapper function.
public static <T> List<T> splitCommaSeparatedStringToList(String s,
Function<String, T> function) {
if (isNull(s)) {
return Collections.emptyList();
}
return FluentIterable.from(Arrays.asList(s.split(COMMA))).transform(function).toList();
}
only replace List<String> with List<?>
public static List<?> splitCommaSeparatedStringToListAnyDataTypeArray(String s) {
List<String> ints = new ArrayList<>();
if (isNull(s)) {
return ints;
}
String[] split = s.split(COMMA);
for (String st : split) {
ints.add(String.valueOf(st));
}
return ints;
}
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.
In this method I get string as input and according to the string name I need to return value sometimes its string sometime int ,double,int64 ,bool etc
Since its dynamic type i don't know how to define it in the method return type
and how to add to it the value and how to call to this method that the return type is dynamic ,any idea?
public static ? SwitchInput(String TypeName) {
if (TypeName == "java.lang.String" ) {
Return = "A";
}
else if (TypeName == "int" ) {
Return = 1;
}
else if (TypeName == "double") {
Return = 1.00
}
etc for bool and all the other types
}
Object will be your best bet, unless returned type shares an Ancestor
Example :
public static Object switchInput(String typeName) {
if ("java.lang.String".equals(typeName)) {
return "A";
}
else if ("int".equals(typeName)) {
return 1i;
}
else if ("double".equals(typeName)) {
return 1.0d
}
}
Another example with generics
static <T> T switchInput(String typeName){
if ("java.lang.String".equals(typeName)) {
return "A";
}
else if ("int".equals(typeName)) {
return 1i;
}
else if ("double".equals(typeName)) {
return 1.0d
}
}
String str = MyClass.switchInput("java.lang.String")
I have not tested that, this is a simpler version of my first thought about generics
To know what the return type is, you have to find a container where all these types fit in. Obviously, this is Object. You'd have to convert the primitive types to the corresponding object (like int to Integer).
A better approach would be to create a new container class, which holds a generic type <T>. Like
public class SwitchDemo {
public static SwitchInputType<?> switchInput(String typeName) {
if (typeName.equals("java.lang.String")) {
return new SwitchInputType<String>(new String("A"));
} else if (typeName.equals("int")) {
return new SwitchInputType<Integer>(new Integer(312));
}
return null;
}
public static class SwitchInputType<T> {
private T type;
public SwitchInputType(T type) {
super();
this.type = type;
}
public T getType() {
return type;
}
public void setType(T type) {
this.type = type;
}
}
public static void main(String[] args) {
SwitchInputType<?> sit1 = SwitchDemo.switchInput("java.lang.String");
System.out.println(sit1.getType());
SwitchInputType<?> sit2 = SwitchDemo.switchInput("int");
System.out.println(sit2.getType());
}
}
As an ugly solution to your problem, you could set your method to run the type Object. (as Boolean, Integer, Double are all subtypes)
You would have to ensure though that you then inferred the correct type afterwards when using the returned value (using instanceof) and recast it to the correct type.
Can I ask though why you need such a method? This is abusing the notion of a method definition slightly.
public static Object SwitchInput(String TypeName) {
if (TypeName.equals("java.lang.String") ) {
Return = new String("A");
}
else if (TypeName.equals("int") ) {
Return = new Integer(1);
}
else if (TypeName.equals("double")) {
Return = new Double(1.00) ;
}
etc for bool and all the other types
}
And using this code snippet to infer what type it is further on down in your code
if(returned_value instanceof Double)
etc.
I have a class Data<T>
with a generic attribute
private T value;
is there nicer way to do the following?
ie returning the generic type in different forms?
public List<String> getValues() {
if (value.getClass() != ArrayList.class)
throw new Exception("Wrong Enum value '%s'", value);
return (ArrayList<String>) value;
//ugly
}
public String getStringValue() {
if (value.getClass() != String.class)
throw new Exception("Wrong value type '%s'", value);
return (String) value;
//ugly
}
public Float getFloatValue() {
if (value.getClass() != Double.class)
throw new Exception("Wrong value type '%s'", value);
return (Float) value;
//ugly
}
public Long getLongValue() {
if (value.getClass() != Double.class)
throw new Exception("Wrong value type '%s'", value);
return (Long) value;
//ugly
}
public T getValue() {
return value;
}
Precision, I'm using Gson as deserializer, to get a List, each Data objects can then be heterogeous
Could also register adapters for float and long detection, but it wouldn't be faster or nicer
edit: gson fails to retrieve longs:
either:
((Long) d.getValue())
java.lang.Double cannot be cast to java.lang.Long
or
Long.parseLong( d.getValue().toString())
java.lang.NumberFormatException: For input string: "212231.0"
I tried to register a LongAdpater
gsonBuilder.registerTypeAdapter(Long.class, new LongAdapter());
private static class LongAdapter implements
JsonSerializer<Long>, JsonDeserializer<Long>
{
#Override public Long deserialize(
JsonElement json,
Type type,
JsonDeserializationContext arg2) throws JsonParseException
{
return json.getAsLong();
}
#Override
public JsonElement serialize(Long l, Type arg1,
JsonSerializationContext arg2) {
return new JsonPrimitive(new Double(l));
}
}
java.lang.IllegalArgumentException: Cannot register type adapters for class java.lang.Long
edit2 for tsOverflow:
Data<Float> d1 = new Data<Float>( new Float(6.32));
List<String> l = new ArrayList<String>();
l.add("fr");
l.add("it");
l.add("en");
Data<List<String>> d2 = new Data<List<String>>( l);
Data<Long> d3 = new Data<Long>(new Long(212231));
List<Data> data = new ArrayList<Data>();
data.add(d1);
data.add(d2);
data.add(d3)
new Gson().toJson(data);
The point of generics is NOT to allow a class to use different types at the same time.
Generics allow you to define/restrict the type used by an instance of an object.
The idea behind generics is to eliminate the need to cast.
Using generics with your class should result in something like this:
Data<String> stringData = new Data<String>();
String someString = stringData.getValue();
Data<Long> longData = new Data<Long>();
Long someLong = longData.getValue();
Data<List<String>> listData = new Data<List<String>>();
List<String> someList = listData.getValue();
You should either use Objects and casting --OR-- use generics to avoid casting.
You seem to believe that generics allow for heterogeneous typing within the same instance.
That is not correct.
If you want a list to contain a mixed bag of types, then generics are not appropriate.
Also...
To create a long from a double, use Double.longValue().
To create a float from a double, use Double.floatValue().
I recommend reading the documentation.
The design looks suspicious to me, but to answer your actual question:
The case for Long-values looks wrong. Your snippet contains a c&p error
public Long getLongValue() {
if (value.getClass() != Double.class) // <<- should be Long.class
throw new Exception("Wrong value type '%s'", value);
return (Long) value;
//ugly
}
thus it should read:
public Long getLongValue() {
if (value.getClass() != Long.class)
throw new Exception("Wrong value type '%s'", value);
return (Long) value;
//ugly
}
However, in order to reduce code duplication, you could introduce a generic helper method
private T getValue() {
return value;
}
private <V> V castValue(Class<V> type) {
if (!type.isInstance(value)) {
// exception handling
}
return type.cast(value);
}
public List<String> getValues() {
return castValue(ArrayList.class);
}
public String getStringValue() {
return castValue(String.class);
}
If you decide to go for that approach, I'd recommend to de-generify the data class since it's irritating to have a type parameter if there is actually no constraint on the instance itself. I'd use Object instead for the field type:
private Object getValue() {
return value;
}
private <V> V castValue(Class<V> type) {
if (!type.isInstance(value)) {
// exception handling
}
return type.cast(value);
}
public List<String> getValues() {
return castValue(ArrayList.class);
}
public String getStringValue() {
return castValue(String.class);
}
// .. more cases ..
You could just use the type T directly for a simple getter and Class.cast -method for other types:
public class GenericDataTest
{
private static class DataTest<T>
{
private T value;
public DataTest(T value)
{
this.value = value;
}
public T getValue()
{
return value;
}
public Object getValueAsType(Class<?> type)
{
return type.cast(value);
}
}
#Test
public void testGeneric()
{
DataTest<String> stringTest = new DataTest<String>("Test");
Assert.assertEquals("Test", stringTest.getValue());
Assert.assertEquals("Test", stringTest.getValueAsType(String.class));
DataTest<Double> doubleTest = new DataTest<Double>(1.0);
Assert.assertEquals(1.0, doubleTest.getValue());
Assert.assertEquals(1.0, doubleTest.getValueAsType(Double.class));
}
#Test(expected = ClassCastException.class)
public void testClassCastFailure()
{
DataTest<String> stringTest = new DataTest<String>("Test");
Assert.assertEquals("Test", stringTest.getValueAsType(Float.class));
}
}
You could ask if "value" is assignable to the expected class.
private T value;
.
.
.
public Object getValueAsObjectOfClass(Class<?> expectedClass) {
if(!expectedClass.isAssignableFrom(value.getClass())) {
// abort gracefully
}
return expectedClass.cast(value);
}