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)))
Related
Is there a nice way to iterate over object fields using reflection?
The main problem is in object can be another object therefore it's needed to iterate over another object's properties too.
For example I have AllInclusiveDetails object
public class AllInclusiveDetails {
#JsonProperty("renters_business")
private String rentersBusiness;
#Valid
#NotNull
#JsonProperty("main_property_owner_details")
private ShortCustomer mainPropertyOwnerDetails;
#Valid
#NotNull
#JsonProperty("main_tenant_details")
private ShortCustomer mainTenantDetails;
}
And ShortCustomer is
public class ShortCustomer {
#NotNull
#Positive
private Long id;
#NotEmpty
#JsonProperty("full_name")
private String fullName;
#JsonProperty("organization_number")
private String organizationNumber;
#PastOrPresent
private LocalDate birthdate;
}
I want to iterate over AllInclusiveDetails object fields using reflection and if there is another object in it , I want to iterate over that object fields too.
The main purpose is to track if value of the same field in two different object are equal or not and if not to save old value and the new one.
Does this satisfy your requirement:
for(Field field : AllInclusiveDetails.class.getDeclaredFields()) {
if(field.getType()== ShortCustomer.class) {
//Do your logic here
}
}
Here's a way to get all the fields of a class and a method to recursively use reflection to compare fields. Play around with main to test it, it will not work correctly for Objects that are logically equivalent but not the same in memory.
// Gathers all fields of this class, including those in superclasses, regardless of visibility
public static List<Field> getAllFields(Class<?> klass) {
List<Field> fields = new ArrayList<>();
for (Class<?> k = klass; k != null; k = k.getSuperclass()) {
fields.addAll(Arrays.asList(k.getDeclaredFields()));
}
return fields;
}
// Uses reflection and recursion to deep compare two objects.
// If the sub-fields and sub-arrays are not deeply equal this will return false.
// This will cause problems with data structures that may be logically equivalent
// but not have the same structure in memory, HashMaps and Sets come to mind
//
// Also might perform illegal reflective access which gets a warning from the JVM
// WARNING: Illegal reflective access ... to field java.util.LinkedList.size
// WARNING: Please consider reporting this to the maintainers ...
// WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
// WARNING: All illegal access operations will be denied in a future release
public static <T> boolean reflexiveEquals(T o1, T o2) {
return reflexiveEquals(o1, o2, new HashSet<>(), new HashSet<>());
}
private static <T> boolean reflexiveEquals(T o1, T o2, Set<Object> o1Refs, Set<Object> o2Refs) {
if (o1 == o2) {
// exact same object or both are null
return true;
}
if (o1 == null || o2 == null) {
// one is null but the other is not
System.err.println(o1 + " != " + o2);
return false;
}
Class<?> type = o1.getClass();
if (type != o2.getClass()) {
// not the exact same class therefore not equal
// you could treat this differently if you want
System.err.println(type + " != " + o2.getClass());
return false;
}
if (PRIMITIVE_WRAPPERS.contains(type)) {
// if it's a primitive wrapper then compare plainly
boolean result = Objects.equals(o1, o2);
if (!result) {
System.err.println("Objects.equals: " + o1 + " : " + o2);
}
return result;
}
// before descending, make sure there wont be an infinite loop
// if this object appeared in the reference chain before
// then it is currently being compared lower in the stack,
// return true to let it finish it's comparison
if (o1Refs.contains(o1) || o2Refs.contains(o2)) {
return true;
}
try {
// keep track of the objects that have been descended into
o1Refs.add(o1);
o2Refs.add(o2);
if (type.isArray()) {
// if its an array, compare all elements
try {
Object[] a1 = (Object[]) o1;
Object[] a2 = (Object[]) o2;
// only comparable field besides elements
if (a1.length != a2.length) {
System.err.println("Array length diff");
return false;
}
for (int i = 0; i < a1.length; i++) {
if (!reflexiveEquals(a1[i], a2[i], o1Refs, o2Refs)) {
return false;
}
}
return true;
} catch (Exception e) {
return false;
}
}
// otherwise its some other object so compare all fields
// moving up the super-classes as well
for (Class<?> k = type; k != null; k = k.getSuperclass()) {
for (Field f : k.getDeclaredFields()) {
try {
f.setAccessible(true);
if (!reflexiveEquals(f.get(o1), f.get(o2), o1Refs, o2Refs)) {
return false;
}
} catch (IllegalArgumentException | IllegalAccessException e) {
return false;
}
}
}
return true;
} finally {
// remove the references since their compare is complete
o1Refs.remove(o1);
o2Refs.remove(o2);
}
}
private static final Set<Class<?>> PRIMITIVE_WRAPPERS = getPrimitiveWrapperClasses();
private static final Set<Class<?>> getPrimitiveWrapperClasses() {
Set<Class<?>> set = new HashSet<>();
set.add(Boolean.class);
set.add(Character.class);
set.add(Byte.class);
set.add(Short.class);
set.add(Integer.class);
set.add(Long.class);
set.add(Float.class);
set.add(Double.class);
set.add(Void.class);
return set;
}
public static class AllInclusiveDetails {
private String rentersBusiness;
private ShortCustomer mainPropertyOwnerDetails;
private ShortCustomer mainTenantDetails;
private ShortCustomer[] arr;
private List<ShortCustomer> list;
}
public static class ShortCustomer {
private Long id;
private String fullName;
private String organizationNumber;
private LocalDate birthdate;
}
public static void main(String[] args) {
AllInclusiveDetails aids1 = new AllInclusiveDetails();
aids1.rentersBusiness = "Business";
aids1.mainTenantDetails = new ShortCustomer();
aids1.mainTenantDetails.id = 1L;
aids1.mainTenantDetails.fullName = "John Doe";
aids1.arr = new ShortCustomer[] {
aids1.mainTenantDetails,
aids1.mainPropertyOwnerDetails };
aids1.list = new LinkedList<>(Arrays.asList(aids1.arr));
AllInclusiveDetails aids2 = new AllInclusiveDetails();
aids2.rentersBusiness = "Business";
aids2.mainTenantDetails = new ShortCustomer();
aids2.mainTenantDetails.id = 1L;
aids2.mainTenantDetails.fullName = "John Doe";
aids2.arr = new ShortCustomer[] {
aids2.mainTenantDetails,
aids2.mainPropertyOwnerDetails };
aids2.list = new LinkedList<>(Arrays.asList(aids2.arr));
System.out.println(reflexiveEquals(aids1, aids2));
}
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);
}
}
I have a class that I want to be instantiated with either a String or int value, and I define the corresponding instance variable value as generic type T:
public class MathValue<T extends Object> {
private boolean isOperand, isOperator;
// a generic-typed instance variable:
private T value;
// constructor:
public MathValue(int operand) {
// compile-error -- "Incompatible types: required T, found Integer"
this.value = new Integer(operand);
this.isOperand = true;
this.isOperator = false;
}
// constructor:
public MathValue(String operator) {
// compile-error -- "Incompatible types: required T, found String"
this.value = operand;
this.isOperand = false;
this.isOperator = true;
}
}
I could very well have a single constructor instead, with a formal parameter of type T, but I want to enforce the class' instantiation with a String or int argument:
public class MathValue<T extends Object> {
private boolean isOperand, isOperator;
// a generic-typed instance variable:
private T value;
// it totally works, but does not enforce specific-typed instantiation:
public MathValue(T operandOrOperator) {
this.value = operandOrOperator;
if (operandOrOperator instanceof Integer) {
this.isOperand = true;
this.isOperator = false;
} else if (operandOrOperator instanceof String) {
this.isOperand = false;
this.isOperator = true;
}
}
}
So despite the logical error of wanting to make a generic-typed class "not so generic", is it possible to instantiate a generic variable with a specific-typed value?
You could create factory methods. Here's now it might look:
public class MathValue<T extends Object> {
public static MathValue<String> from(String s) {
MathValue<String> mv = new MathValue<String>();
mv.setValue(s);
mv.setIsOperand(true);
return mv;
}
public static MathValue<Integer> from(Integer s) {
MathValue<Integer> mv = new MathValue<Integer>();
mv.setValue(i);
mv.setIsOperand(false);
return mv;
}
// Rest of your class below
}
If you absolutely need a constructor (eg, you don't know the type that you're creating for ahead of time), then I can't see a way around #RohitJain 's suggestion.
You could use sub-classes. Define an abstract class to actually store the value, generically:
abstract class MathValue<T> {
private final T value;
MathValue(T value) {
this.value = value;
}
abstract boolean isOperator();
boolean isOperand() {
return !isOperator();
}
}
Then one subclass that enforces the value type to be an integer.
class OperandValue extends MathValue<Integer> {
OperandValue(int operand) {
super(new Integer(operand));
}
#Override
public boolean isOperator() {
return false;
}
}
and another subtype that enforces it to be a String.
class OperatorValue extends MathValue<String> {
OperatorValue(String operator) {
super(operator);
}
#Override
boolean isOperator() {
return true;
}
}
With this design, you don't actually need to store the Booleans.
(Note, for simplicity I left out the visibility keywords.)
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);
}