Restricting types not related through inheritance without using instanceOf
by creating own class hierarchy. It needs to interact with a key value data store, will convert to object before putting, but needs to convert back to fields after getting from DB with Function<Object,Field>
. Something about this with regards to inheritance doesn't seem right, although it works.
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
/**
* Field, placeholder for a type that can be stored in db
*/
@EqualsAndHashCode
public class Field<T> {
@Getter
private T featureValue;
Field(@NonNull T value) {
this.featureValue = value;
}
public static class Factory {
}
}
//convert object to feature value (since ddb returns Map<String, Object> on Item.asMap)
//convert particular type of object to feature value (say Integer, String)
//abstract Field from(T value);
//convert feature value to particular type, call from drvd (from base => obj)
//enum or getEnum for some type expressed as string factory
/**
* Field for a boolean type
*/
public class BooleanField extends Field<Boolean> {
public BooleanField(Boolean value) {
super(value);
}
public static BooleanField fromObject(Object value) {
return new BooleanField((Boolean) value);
}
}
import java.util.List;
/**
* Field for a list data type, holds a list of Field
*/
public class ListField extends Field<List<Field>> {
public ListField(List<Field> value) {
super(value);
}
public static ListField fromObject(Object value) {
return new ListField((List<Field>) value);
}
}
import java.util.Map;
/**
* Field for a map type, holds a map of String to Field
*/
public class MapField extends Field<Map<String, Field>> {
public MapField(Map<String, Field> value) {
super(value);
}
public static MapField fromObject(Object value) {
return new MapField((Map<String, Field>) value);
}
}
/**
* Field for a Numeric type
*/
public class NumericField extends Field<Number> {
public NumericField(Number value) {
super(value);
}
public static NumericField fromObject(Object value) {
return new NumericField((Number) value);
}
}
import java.util.Set;
/**
* Field for a set data type
*/
public class SetField extends Field<Set<Field>> {
public SetField(Set<Field> value) {
super(value);
}
public static SetField fromObject(Object value) {
return new SetField((Set<Field>) value);
}
}
/**
* Field for a string type
*/
public class StringField extends Field<String> {
public StringField(String value) {
super(value);
}
public static StringField fromObject(Object value) {
return new StringField((String) value);
}
}
1 Answer 1
You can create the same thing by not creating all those subclasses. I also see that you can't use the instanceOf
method. My first intention was getting this test method to work:
System.out.println(Field.fromObject(new ArrayList()).getFeatureValue().getClass());
System.out.println(Field.fromObject(new Integer(6)).getFeatureValue());
System.out.println(Field.fromObject(new Integer(6)).getFeatureValue().getClass());
System.out.println(Field.fromObject(Boolean.TRUE)); // null or error
As you can see, I call Field
and not an implementation of the Field
class. But I also want the correct class back as what class I put in there, so the output should be:
class java.util.ArrayList
6
class java.lang.Integer
null
In order that Boolean
is a type what can't be created. After searching on this topic you come always back to instanceOf
or what you did. The thing what I wanted is something I can refactor really easy, so it needed to be on 1 place to refactor.
Enum
could help me with that, but still having trouble with subclasses what also need to be valid. (ArrayList
if List
is allowed)
I refactored your code to this. If you need to create the subclasses, you could still look at the static
method.
public class Field<T> {
private T featureValue;
Field(T value) {
this.featureValue = value;
}
public T getFeatureValue() {
return featureValue;
}
public static <T> Field<T> fromObject(T value) {
for (Allowed allow : Allowed.values()) {
if (allow.getClazz().isAssignableFrom(value.getClass())) {
return new Field<>(value);
}
}
return null;
}
private enum Allowed {
INTEGER(Integer.class),
STRING(String.class),
LIST(List.class);
private final Class clazz;
private Allowed(Class clazz) {
this.clazz = clazz;
}
public Class getClazz() {
return clazz;
}
}
}
As you can see, we iterate the Enum
values to check the restrictions. With this solution you can add also the following test:
List<String> list = Field.fromObject(new ArrayList<String>()).getFeatureValue();
As you can see, no casting is required.
The next is broken code:
List<String> list = Field.fromObject(new ArrayList<Boolean>()).getFeatureValue();
which is also good because these are 2 different types.
-
\$\begingroup\$ Thanks, this still allows the list to contain any type since the compiler gives an error "cannot select from parameterized type" when
List<String>.class
is added to the Allowed enum. I have put up what the what my code was supposed to replace here \$\endgroup\$shrewquest– shrewquest2016年01月20日 02:38:04 +00:00Commented Jan 20, 2016 at 2:38 -
\$\begingroup\$ Yeah, it's no inheritance anymore. I had a suspicion it was for soms online test. Do the create function need to be static? \$\endgroup\$chillworld– chillworld2016年01月20日 03:40:19 +00:00Commented Jan 20, 2016 at 3:40
-
\$\begingroup\$ no it doesn't I was trying to restrict which subclasses can be created by making the base class ctor package access and using a factory pattern (which is not in the code yet), also see the comments after the Field class \$\endgroup\$shrewquest– shrewquest2016年01月20日 05:07:53 +00:00Commented Jan 20, 2016 at 5:07
instanceOf
you get stuck. see this question : stackoverflow.com/questions/34893297/… or is there any other way to know what type of object it is? \$\endgroup\$