1
\$\begingroup\$

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);
    }
}
\$\endgroup\$
  • \$\begingroup\$ Do your use case say that you need all those implementations or is this done only for having the fromObject method? \$\endgroup\$ – chillworld Jan 19 '16 at 11:08
  • \$\begingroup\$ I have rolled back the last edit. Please see What to do when someone answers. \$\endgroup\$ – Mast Jan 20 '16 at 2:05
  • \$\begingroup\$ I have put up what the what this code was supposed to replace here, it is mostly to avoid excessive if else as in the link \$\endgroup\$ – shrewquest Jan 20 '16 at 2:40
  • \$\begingroup\$ I'm trying make a better answer but without any 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\$ – chillworld Jan 20 '16 at 7:13
4
\$\begingroup\$

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.

\$\endgroup\$
  • \$\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 Jan 20 '16 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 Jan 20 '16 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 Jan 20 '16 at 5:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.