Java - Generics vs Casting Objects - java

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);
}

Related

Enum with other enum as type

I want to have an Enum like this:
public enum Type {
STRING, INTEGER, BOOLEAN, LIST(Type);
Type t;
Type() { this.t = this; )
Type(Type t) { this.t = t; }
}
Such that I can enter various Types for LIST, like being able to call Type.LIST(STRING). Is this possible in Java?
enums are limited, you can't have an unknown amount of entries. So you can't have LIST(LIST(LIST(LIST(...))) as a separate Type enum. You'll need a class, but that doesn't mean you have to instantiate lots of objects necessarily:
It may be premature optimization, but you can use a flyweight pattern to ensure that you can't get more than one instance of a Type:
package com.example;
public final class Type {
public enum LeafType {
STRING,
INTEGER,
BOOLEAN
}
//Gives you the familiar enum syntax
public static final Type STRING = new Type(LeafType.STRING);
public static final Type INTEGER = new Type(LeafType.INTEGER);
public static final Type BOOLEAN = new Type(LeafType.BOOLEAN);
private final LeafType leafType;
private final Type listType;
private final Object lock = new Object();
// This is the cache that prevents creation of multiple instances
private Type listOfMeType;
private Type(LeafType leafType) {
if (leafType == null) throw new RuntimeException("X");
this.leafType = leafType;
listType = null;
}
private Type(Type type) {
leafType = null;
listType = type;
}
/**
* Get the type that represents a list of this type
*/
public Type list() {
synchronized (lock) {
if (listOfMeType == null) {
listOfMeType = new Type(this);
}
return listOfMeType;
}
}
public boolean isList() {
return listType != null;
}
/**
* If this type is a list, will return what type of list it is
*/
public Type getListType() {
if (!isList()) {
throw new RuntimeException("Not a list");
}
return listType;
}
/**
* If this type is a leaf, will return what type of leaf it is
*/
public LeafType getLeafType() {
if (isList()) {
throw new RuntimeException("Not a leaf");
}
return leafType;
}
#Override
public String toString() {
if (isList()) {
return "LIST(" + getListType() + ")";
}
return getLeafType().toString();
}
}
Usage:
Simple type:
Type string = Type.STRING;
List:
Type stringList = Type.STRING.list();
List of list:
Type stringListList = Type.STRING.list().list();
And you can never get in the situation where you have two instances of Type that describe the same type, e.g.:
Type t1 = Type.BOOLEAN.list().list().list();
Type t2 = Type.BOOLEAN.list().list().list();
System.out.println(t1 == t2 ? "Same instance" : "Not same instance");
I added toString for debugging:
Type listListListInt = Type.INTEGER.list().list().list();
System.out.println(listListListInt);
Gives:
LIST(LIST(LIST(INTEGER)))

Java return type generic

I'm newbie in using generic in java, i have simple method that i would like to return any type from that, this method return other class getters methods, for example i have channels class and that have getChannelId() method, i want to return that from this method:
#SuppressWarnings("unchecked")
public <T extends Channels> T getChannelColumn(ChannelContentModel.channelColumns column) {
switch (column) {
case id:
return (T) channel.getId();
break;
case title:
return (T) channel.getChannelTitle();
break;
}
return null;
}
getId is int and getChannelTitle is string, how can i fix this method to return any type?
in my code channel is instance of Channels class, Thanks in advance
As commented by #Geckstar;
Java is statically typed
But it is possible to clear your code sample from compile errors.
Remove breaks, because there is no need to break after return statement.
extend return type from object. T extends Object instead T extends Channel
id must be Integer, not int or change to (T)(Integer) channel.getId()
This is not a good solution, it only fixes compile errors.But this method's usage may cause runtime failures.
#SuppressWarnings("unchecked")
public <T extends Object> T getChannelColumn(ChannelContentModel.channelColumns column) {
switch (column) {
case id:
return (T) channel.getId(); //int must be Integer
case title:
return (T) channel.getChannelTitle();
}
return null;
}
You can create property wrapper which will hold the value in string data format when required it will convert to a specific data type.
public class MyProperty {
private String stringVal;
public MyProperty(String stringVal) {
this.stringVal = stringVal;
}
public Integer getInteger() throws IllegalArgumentException {
return integerValue.getValue();
}
public String getString() {
return stringValue.getValue();
}
private PropertyValue<String> stringValue = new PropertyValue<String>() {
protected String parse(String rep) {
return rep;
}
};
private PropertyValue<Integer> integerValue = new PropertyValue<Integer>() {
protected Integer parse(String rep) throws NumberFormatException {
return Integer.valueOf(rep);
}
};
private abstract class PropertyValue<T> {
private T value;
protected abstract T parse(String rep) throws Exception;
public T getValue() throws IllegalArgumentException {
try {
value = (stringValue == null) ? null : parse(stringVal);
} catch (Exception e) {
value = null;
}
return value;
}
}
}
you can make specialize class or one class having the access method to get the value with specified data type
#SuppressWarnings("unchecked")
public MyProperty getChannelColumn(ChannelContentModel.channelColumns column) {
switch (column) {
case id:
return new MyProperty(channel.getId());//
break;
case title:
return new MyProperty(channel.getChannelTitle());
break;
}
return null;
}

Howto distinguish between not-provided and null when deserializing JSON

We send JSON strings from our front end as input to our java code. The Java side turns that into beans using Gson. Now my front end guy approached me with these requirements:
sometimes he wants to pass a new value, that the backend simply writes into the database
sometimes he wants to pass no value, which tells the backend to not do anything about this value
sometimes he wants to pass a null, which tells the backend to reset to some "default value" (known to the backend, but the front end doesn't care about it)
it should also work with strings, numbers, boolean, ...
We came up with this idea:
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.lang.reflect.Type;
import java.util.Objects;
import org.junit.Test;
import com.google.gson.*;
class ResetableValue<T> {
private static enum Content {
VALUE, RESET, NOT_PROVIDED
};
private final T value;
private final Content content;
public ResetableValue(T value) {
this(value, Content.VALUE);
}
private ResetableValue(T value, Content content) {
this.value = value;
this.content = content;
}
static <T> ResetableValue<T> asReset() {
return new ResetableValue<>(null, Content.RESET);
}
static <T> ResetableValue<T> asNotProvided() {
return new ResetableValue<>(null, Content.NOT_PROVIDED);
}
T getValue() {
if (content != Content.VALUE) {
throw new IllegalStateException("can't provide value for " + content);
}
return value;
}
boolean isReset() {
return content == Content.RESET;
}
boolean isNotProvided() {
return content == Content.NOT_PROVIDED;
}
#Override
public String toString() {
if (content == Content.VALUE) {
return Objects.toString(value);
}
return content.toString();
}
}
class ResetableValueDeserializer implements JsonDeserializer<ResetableValue<String>> {
public ResetableValue<String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return new ResetableValue<String>(json.getAsJsonPrimitive().getAsString());
}
}
class ExampleBean {
private ResetableValue<String> property = ResetableValue.asNotProvided();
public ResetableValue<String> getProperty() {
if (property == null) {
return ResetableValue.asReset();
}
return property;
}
#Override
public String toString() {
return "property: " + Objects.toString(property);
}
}
public class GsonStuffTest {
Gson gson = new GsonBuilder().registerTypeAdapter(ResetableValue.class, new ResetableValueDeserializer()).create();
#Test
public void testValue() {
String serializedContent = "{\"property\":\"foo\"}";
ExampleBean bean = gson.fromJson(serializedContent, ExampleBean.class);
assertThat(bean.getProperty().getValue(), is("foo"));
}
#Test
public void testIsNotProvided() {
String serializedContent = "{}";
ExampleBean bean = gson.fromJson(serializedContent, ExampleBean.class);
assertThat(bean.getProperty().isNotProvided(), is(true));
}
#Test
public void testIsReset() {
String serializedContent = "{\"property\":null}";
ExampleBean bean = gson.fromJson(serializedContent, ExampleBean.class);
assertThat(bean.getProperty().isReset(), is(true));
}
}
Please note: the idea is of course to have multiple different fields of that type ResetableValue in a bean. And then one field might care a value, one is omitted, another is set to to null.
Question(s):
The above example "works" - but I really dislike the fact that I have to handle the "reset" case within the getProperty() method of my bean. As that means: it is not enough to have a custom deserializer, I also need to put that special check into any getter method. So: are there more elegant solutions to this? Is there a way to have Gson distinguish between "a property is not showing up" vs. "a property is set to null"?
The above example claims to be generic; but obviously the deserializer code only works for string properties. Is there a way to make this "really generic"?
I guess a different way to express my question is: is there something like "Optionals" support when deserializing JSON into beans using Gson?
The above example "works" - but I really dislike the fact that I have to handle the "reset" case within the getProperty() method of my bean. As that means: it is not enough to have a custom deserializer, I also need to put that special check into any getter method. So: are there more elegant solutions to this? Is there a way to have Gson distinguish between "a property is not showing up" vs. "a property is set to null"?
Sort of. Your getProperty seems to have a redundant check: it should never check for null and just return the property field in any case assuming that Gson managed to instantiate it.
The above example claims to be generic; but obviously the deserializer code only works for string properties. Is there a way to make this "really generic"?
Yes, via type adapter factories and type adapters (regarding the latter: JsonSerializer and JsonDeserializer classes use JSON trees consuming more memory, but type adapters are stream-fashioned and consume much less).
Let's consider you have a generic tri-state value holder like the following one.
I would also hide away the constructor to make it more fluent and encapsulate the way it's instantiated (or not instantiated).
final class Value<T> {
private static final Value<?> noValue = new Value<>(State.NO_VALUE, null);
private static final Value<?> undefined = new Value<>(State.UNDEFINED, null);
private enum State {
VALUE,
NO_VALUE,
UNDEFINED
}
private final State state;
private final T value;
private Value(final State state, final T value) {
this.value = value;
this.state = state;
}
static <T> Value<T> value(final T value) {
if ( value == null ) {
return noValue();
}
return new Value<>(State.VALUE, value);
}
static <T> Value<T> noValue() {
#SuppressWarnings("unchecked")
final Value<T> value = (Value<T>) noValue;
return value;
}
static <T> Value<T> undefined() {
#SuppressWarnings("unchecked")
final Value<T> value = (Value<T>) undefined;
return value;
}
T getValue()
throws IllegalStateException {
if ( state != State.VALUE ) {
throw new IllegalStateException("Can't provide value for " + state);
}
return value;
}
boolean isValue() {
return state == State.VALUE;
}
boolean isNoValue() {
return state == State.NO_VALUE;
}
boolean isUndefined() {
return state == State.UNDEFINED;
}
#Override
public String toString() {
if ( state != State.VALUE ) {
return state.toString();
}
return Objects.toString(value);
}
}
Next, define a simple data bag to hold the values.
Note that you must either declare them as undefined() in order to preserve the null-object semantics, or assign it a null so Gson would take care of it below (you choose).
final class DataBag {
final Value<Integer> integer = null;/* = undefined()*/
final Value<String> string = null;/* = undefined()*/
private DataBag() {
}
}
Some reflection utilities code here to analyze type parameterization and build sub-to-super class hierarchy iterators (I don't know how to create a Java 8 stream from scratch yet):
final class Reflection {
private Reflection() {
}
static Type getTypeParameter0(final Type type) {
if ( !(type instanceof ParameterizedType) ) {
return Object.class;
}
final ParameterizedType parameterizedType = (ParameterizedType) type;
return parameterizedType.getActualTypeArguments()[0];
}
static Iterable<Class<?>> subToSuperClass(final Class<?> subClass) {
return subToSuperClass(Object.class, subClass);
}
static <SUP, SUB extends SUP> Iterable<Class<?>> subToSuperClass(final Class<SUP> superClass, final Class<SUB> subClass) {
if ( !superClass.isAssignableFrom(subClass) ) {
throw new IllegalArgumentException(superClass + " is not assignable from " + subClass);
}
return () -> new Iterator<Class<?>>() {
private Class<?> current = subClass;
#Override
public boolean hasNext() {
return current != null;
}
#Override
public Class<?> next() {
if ( current == null ) {
throw new NoSuchElementException();
}
final Class<?> result = current;
current = result != superClass ? current.getSuperclass() : null;
return result;
}
};
}
}
ValueTypeAdapterFactory
ValueTypeAdapterFactory is responsible for any generic values by delegating (de)serialization process to downstream type adapters.
final class ValueTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory valueTypeAdapterFactory = new ValueTypeAdapterFactory();
private ValueTypeAdapterFactory() {
}
static TypeAdapterFactory getValueTypeAdapterFactory() {
return valueTypeAdapterFactory;
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !Value.class.isAssignableFrom(typeToken.getRawType()) ) {
return null;
}
final Type valueTypeParameter = getTypeParameter0(typeToken.getType());
// Some boring Java unchecked stuff here...
#SuppressWarnings("unchecked")
final TypeAdapter<Object> innerTypeAdapter = (TypeAdapter<Object>) gson.getDelegateAdapter(this, TypeToken.get(valueTypeParameter));
final TypeAdapter<Value<Object>> outerTypeAdapter = new ValueTypeAdapter<>(innerTypeAdapter);
#SuppressWarnings("unchecked")
final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) outerTypeAdapter;
return typeAdapter;
}
private static final class ValueTypeAdapter<T>
extends TypeAdapter<Value<T>> {
private final TypeAdapter<T> innerTypeAdapter;
private ValueTypeAdapter(final TypeAdapter<T> innerTypeAdapter) {
this.innerTypeAdapter = innerTypeAdapter;
}
#Override
public void write(final JsonWriter out, final Value<T> value)
throws IOException {
if ( value.isValue() ) {
final T innerValue = value.getValue();
innerTypeAdapter.write(out, innerValue);
return;
}
// Considering no-value is undefined in order not to produce illegal JSON documents (dangling property names, etc)
if ( value.isNoValue() || value.isUndefined() ) {
innerTypeAdapter.write(out, null);
return;
}
throw new AssertionError();
}
#Override
public Value<T> read(final JsonReader in)
throws IOException {
final JsonToken token = in.peek();
if ( token == NULL ) {
in.nextNull();
return noValue();
}
return value(innerTypeAdapter.read(in));
}
}
}
PostValueTypeAdapterFactory
PostValueTypeAdapterFactory is responsible for "adjusting" POJOs that have null-initialized Value fields by using reflection.
By not registering this factory all Value fields must be initialized with undefined() manually.
Any sequential data structures like iterables/collections/lists|sets, maps or arrays are not implemented here for simplicity.
final class PostValueTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory postValueTypeAdapterFactory = new PostValueTypeAdapterFactory();
private PostValueTypeAdapterFactory() {
}
static TypeAdapterFactory getPostValueTypeAdapterFactory() {
return postValueTypeAdapterFactory;
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final List<Field> valueFields = collectValueFields(typeToken.getRawType());
if ( valueFields.isEmpty() ) {
return null;
}
final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
return new PostValueTypeAdapter<>(delegateTypeAdapter, valueFields);
}
// Just scan class the whole type hierarchy (except java.lang.Object) to find any occurrences of Value<T> fields
private static List<Field> collectValueFields(final Class<?> type) {
return StreamSupport.stream(subToSuperClass(type).spliterator(), false)
.filter(clazz -> clazz != Object.class)
.flatMap(clazz -> Stream.of(clazz.getDeclaredFields()))
.filter(field -> field.getType() == Value.class)
.peek(field -> field.setAccessible(true))
.collect(toImmutableList());
}
private static final class PostValueTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegateTypeAdapter;
private final List<Field> valueFields;
private PostValueTypeAdapter(final TypeAdapter<T> delegateTypeAdapter, final List<Field> valueFields) {
this.delegateTypeAdapter = delegateTypeAdapter;
this.valueFields = valueFields;
}
#Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegateTypeAdapter.write(out, value);
}
#Override
public T read(final JsonReader in)
throws IOException {
try {
final T value = delegateTypeAdapter.read(in);
for ( final Field valueField : valueFields ) {
// A Value<T> field is null? Make it undefined
if ( valueField.get(value) == null ) {
valueField.set(value, undefined());
}
}
return value;
} catch ( final IllegalAccessException ex ) {
throw new IOException(ex);
}
}
}
}
JUnit test:
public final class GsonStuffTest {
private static final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getValueTypeAdapterFactory())
.registerTypeAdapterFactory(getPostValueTypeAdapterFactory())
.create();
#Test
public void testIsValue() {
final DataBag dataBag = parseDataBag("{\"integer\":100,\"string\":\"foo\"}");
assertThat(dataBag.integer.isValue(), is(true));
assertThat(dataBag.integer.getValue(), is(100));
assertThat(dataBag.string.isValue(), is(true));
assertThat(dataBag.string.getValue(), is("foo"));
}
#Test
public void testIsNoValue() {
final DataBag dataBag = parseDataBag("{\"integer\":null,\"string\":null}");
assertThat(dataBag.integer.isNoValue(), is(true));
assertThat(dataBag.string.isNoValue(), is(true));
}
#Test
public void testIsUndefined() {
final DataBag dataBag = parseDataBag("{}");
assertThat(dataBag.integer.isUndefined(), is(true));
assertThat(dataBag.string.isUndefined(), is(true));
}
private static DataBag parseDataBag(final String json) {
return gson.fromJson(json, DataBag.class);
}
}

Switch according to input and return dynamic value

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.

Java Generics and Array types

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]

Categories