简体   繁体   中英

Enum variable inside Enum class

Please help with best approach for following case:

There is table, which has about 20 columns.
Each column has its own short name, full name and type(number or String).
Each column type could have its own operator - For example, String - contains, equals; number - more, less, ==, !=
Each operator could have its own description.

I have to have object of Table class and be able to view all its columns, view short name and full name of each column, use operators based on column type.

I am trying to use enum, but I have no idea, how to connect specific column to specific type.
For example, how to connect "Id" column to "StringType" and "Services" column to "NumberType". Could you please help.

class Table{

    public enum Column {
        Id("id", "ID number"),
        Services("serv", "Services");

        private final String shortName;
        private final String fullName;

        Column(String shortName, String fullName) {
           this.shortName = shortName;
           this.fullName = fullName;
        }

        public String getShortName() {
            return shortName;
        }

        public String getFullName() {
            return fullName;
        }
    }


    public enum StringType{
        contains("String contain another string"),
        equals("String equal a string");

        private final String placeholder;

        StringType(String fullName) {
            this.placeholder = fullName;
        }

        public String getPlaceholder() {
            return placeholder;
        }
    }

    public enum NumberType{
        more("value that is more than input"),
        less("value that is less than input");

        private final String placeholder;

        NumberType(String fullName) {
            this.placeholder = fullName;
        }

        public String getPlaceholder() {
            return placeholder;
        }
    }

}

Like any other class, enum types can implement interfaces. You can use this to your advantage:

public interface DataType {
    // Deliberately empty.  This is a marker interface.
}

public enum StringType
implements DataType {
    // ...
}

public enum NumberType
implements DataType {
    // ...
}

public enum Column {
    Id("id", "ID number", StringType.class),
    Services("serv", "Services", NumberType.class);

    private final String shortName;
    private final String fullName;
    private final Class<? extends DataType> type;

    Column(String shortName, String fullName, Class<? extends DataType> type) {
       this.shortName = shortName;
       this.fullName = fullName;
       this.type = type;
    }

    // ...
}

If you plan to actually use these to compare data, you can add methods to the DataType interface:

public interface DataType<T> {
    Class<T> getDataClass();

    BiPredicate<? super T, ? super T> getTest();

    default boolean test(T value1, T value2) {
        return getTest().test(value1, value2);
    }

    default boolean testObjects(Object value1, Object value2) {
        Class<T> type = getDataClass();
        return test(type.cast(value1), type.cast(value2));
    }
}

public enum StringType
implements DataType<String> {
    contains("String contain another string", String::contains),
    equals("String equal a string", Object::equals);

    private final String placeholder;
    private final BiPredicate<? super String, ? super String> test;

    StringType(String fullName,
               BiPredicate<? super String, ? super String> test) {
        this.placeholder = fullName;
        this.test = test;
    }

    public String getPlaceholder() {
        return placeholder;
    }

    @Override
    public BiPredicate<? super String, ? super String> getTest() {
        return test;
    }

    @Override
    public Class<String> getDataClass() {
        return String.class;
    }
}

public enum NumberType
implements DataType<Number> {
    more("value that is more than input",
        (n1, n2) -> n1.doubleValue() > n2.doubleValue()),
    less("value that is less than input",
        (n1, n2) -> n1.doubleValue() < n2.doubleValue());

    private final String placeholder;
    private final BiPredicate<? super Number, ? super Number> test;

    NumberType(String fullName,
               BiPredicate<? super Number, ? super Number> test) {
        this.placeholder = fullName;
        this.test = test;
    }

    public String getPlaceholder() {
        return placeholder;
    }

    @Override
    public BiPredicate<? super Number, ? super Number> getTest() {
        return test;
    }

    @Override
    public Class<Number> getDataClass() {
        return Number.class;
    }
}

public enum Column {
    Id("id", "ID number", StringType.class),
    Services("serv", "Services", NumberType.class);

    private final String shortName;
    private final String fullName;
    private final Class<? extends DataType<?>> type;

    Column(String shortName, String fullName, Class<? extends DataType<?>> type) {
       this.shortName = shortName;
       this.fullName = fullName;
       this.type = type;
    }

    // ...
}

Java has a Class class that can hold types as its objects, it even works with the primitive types.
It doesn't have a constructor but a factory method called forName() that creates a class using the String provided as its parameter.(although this is considered bad practice).

The better way to obtain a reference to a type is using a class literal . A Class object representing its underlying class can also be obtained from any of its objects using the getClass() method.

Here are some ways through which you can create an object representing a class/type using the Class class: Using Class Literals

    Class<?> type1 = int.class;
    Class<?> type2 = boolean.class;

Factory Method

   Class<?> type1 = Class.forName("java.lang.String"); 

It's better to avoid this method since string parsing is involved which could lead to unnecessary runtime errors as pointed out by @VGR.

Using an object

   String str = "";
   Class type<?> = str.getClass();

You can add an additional variable of type Class<?> and do something like this:


 public enum Column {
        Id("id", "ID number", String.class),
        Services("serv", "Services", Number.class);
        Contact("cont", "Contacts", long.class);

        private final String shortName;
        private final String fullName;
        private final Class<?> type;

        Column(String shortName, String fullName, Class<?> type) {
           this.shortName = shortName;
           this.fullName = fullName;
           this.type = type;
        }

        public String getShortName() {
            return shortName;
        }

        public String getFullName() {
            return fullName;
        }
       public Class<?> getType() {
            return type;
       }
    }

Class is a powerful class, it has various methods like getName() and getMethods() . More on it here .

Note: Class is commonly used for reflection which has major drawbacks, it can basically break encapsulation and also involves some performance overhead. But if you merely use this additional field for the purpose of storing type information, it shouldn't be a big deal.

** This is in assumption that you want to map your column types to Java types!**

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM