How to check against generic enum in Java? - java

Here is my code:
public enum DecisionType {
REFUSAL,
GRANT_OF_PROTECTION,
PARTIAL_REFUSAL;
}
public class DocumentComposition<T extends Enum<DecisionType>> extends TreeMap<DocumentType, Object> {
#Override
public Object put(DocumentType key, Object value) {
if (key.getDecisionType() != ) {
return null;
}
return value;
}
}
DocumentComposition map = new DocumentComposition<DecisionType.REFUSAL>();
I need my Map to contain only elements that are of a certain value of the DecisionType enum. How do I achieve this? What should my test look like?

Do I understand it right you want to have a DocumentComposition which accepts only DocumentType instances of a specific DecisionType ?
My parts of the solution:
You don't need to use generics for that but rather an internal variable which you provide in the constructor.
In you overridden put method you must not forget to call the super otherwise your TreeMap will never get any elements.
public class DocumentComposition extends TreeMap<DocumentType, Object> {
private DecisionType acceptedDecisionType;
public DocumentComposition(DecisionType acceptedDecisionType)
{
this.acceptedDecisionType = acceptedDecisionType;
}
#Override
public Object put(DocumentType key, Object value) {
if (key.getDecisionType() != acceptedDecisionType) {
return null;
}
return super.put(key, value); // do not forget to call super, otherwise your TreeMap is not filled
}
}
Now you can use your map:
public static void main( String args[])
{
DocumentComposition dc=new DocumentComposition(DecisionType.REFUSAL);
dc.put(new DocumentType(DecisionType.REFUSAL), "refusalDoc");
dc.put(new DocumentType(DecisionType.PARTIAL_REFUSAL), "partialRefusalDoc");
System.out.println(dc);
}
Only refusalDoc will be in the map.

Related

Generic BiDiMap

I have a BiDiMap class. How can I make it generic, by accepting not only String but also Object type of objects as input parameters, with keeping all the original functions working. For example I'd like to be able to use function put() with Object, Object as input parameters instead of String, String. I'd like to change all the input parameters and returning values of String type to Object types.
package MyBiDiMap;
import java.util.HashMap;
import java.util.Map;
public class BiDiMap {
private Map<String, String> keyValue;
private Map<String, String> valueKey;
public BiDiMap() {
this.keyValue = new HashMap<>();
this.valueKey = new HashMap<>();
}
private BiDiMap(Map<String, String> keyValue,
Map<String, String> valueKey) {
this.keyValue = keyValue;
this.valueKey = valueKey;
}
public void put(String key, String value) {
if (this.keyValue.containsKey(key)
|| this.valueKey.containsKey(value)) {
this.remove(key);
this.removeInverse(value);
}
this.keyValue.put(key, value);
this.valueKey.put(value, key);
}
public String get(String key) {
return this.keyValue.get(key);
}
public String getInverse(String value) {
return this.valueKey.get(value);
}
public void remove(String key) {
String value = this.keyValue.remove(key);
this.valueKey.remove(value);
}
public void removeInverse(String value) {
String key = this.valueKey.remove(value);
this.keyValue.remove(key);
}
public int size() {
return this.keyValue.size();
}
public BiDiMap getInverse() {
return new BiDiMap(this.valueKey, this.keyValue);
}
}
The answer is pretty simple: by introducing two generic types, named K and V on your class and by then vigorously replacing all occurance of String with K (where your key type should be used), and similarly with V where values are required.
In other words: don't use specific types when declaring the two maps, but in all places, use the new generic types you added on class level.

Cross between an Enum and a Dictionary

In my Java project, I have a need to work with a handful of strings (about 10-30 at a time). I want a data structure to hold them, with properties like so:
Can assign a unique name to each string
The unique names can be used in the code just as if they were variables, with support for IDE auto-complete, no calling getValue() or toString(), etc.
Can iterate over each value in the data structure
In practice, I'd want the code to look something like this:
MagicalDataStructure<String> mds = new MagicalDataStructure(
FirstString = "foo",
SecondString = "bar",
);
/*
This section would output:
foo
bar
*/
for (String value : mds) {
System.out.println(value);
}
/*
This section would output:
The first value is: foo
*/
System.out.println("The first value is: " + FirstString);
Things I've considered:
A class full of static finals. This satisfies #1 and #2, but I can't iterate over them -- at least not without resorting to dark-mojo reflection.
A dictionary. This satisfies #1 and #3, but the keys wouldn't be auto-completable, and there's additional syntax involved in accessing the values.
An enum. This also solves #1 and #3, but accessing the string value takes a little bit of extra code.
Is there a data structure, library, etc that will do what I want?
I would definitely favor a Map for this:
public enum PagePath {
PATH1,
PATH2,
// etc.
}
public static final Map<PagePath, String> ALL_PATHS;
static {
Map<PagePath, String> paths = new EnumMap<>(PagePath.class);
paths.put(PagePath.PATH1, "/html/div[0]/h1");
paths.put(PagePath.PATH2, "/html//form/input[id='firstname']");
// etc.
// Make sure no one breaks things by removing entries
// or by adding enum constants while forgetting to account
// for them in the above Map.
if (!paths.keySet().equals(EnumSet.allOf(PagePath.class))) {
throw new RuntimeException(
"Map does not have entries for all PagePath constants!");
}
ALL_PATHS = Collections.unmodifiableMap(paths);
}
Another possibility, as you’ve mentioned, is using String constants. You can place the initialization of those constants inside the initialization of the “all values” list, to make sure none of them are forgotten:
public static final String PATH1;
public static final String PATH2;
// etc.
public static final Collection<String> ALL_PATHS;
static {
ALL_PATHS = Collections.unmodifiableCollection(Arrays.asList(
PATH1 = "/html/div[0]/h1",
PATH2 = "/html//form/input[id='firstname']",
// etc.
));
}
If someone removes a constant, they’ll be forced to remove its initialization from the Arrays.asList call. If someone adds a constant, and keeps it consistent with the other constants’ declarations, they will be forced to add it to the ALL_PATHS List, since failing to do so would mean it never gets initialized, which compiler will catch.
If your strings are properties your may want to use RessourceBundle or Properties. This can be use to solve problem 1/3.
To solve problem 2, you may create Enum that are Keys to your HashMap so that you need to write hashMap.get(enum) that will auto-complete everything. This solution add words but benefit from auto-completion.
Can you just write a custom method to return the string values using enum?
public enum MagicalDataStructure {
FirstString("foo"),
SecondString("bar");
String value;
MagicalDataStructure(String value) {
this.value = value;
}
public static List<String> getMagicalStrings() {
List<String> strings = new ArrayList<String>();
for (MagicalDataStructure item : MagicalDataStructure.values()) {
strings.add(item.value);
}
return strings;
}
}
And call the function wherever you need to iterate:
public static void main(String[] args) {
for (String magicalString: MagicalDataStructure.getMagicalStrings()) {
System.out.println(magicalString);
}
}
How about this :) The main idea here is the following we use the EnumMap as a base for our CustomEnumMap. My understanding is that you don't need put methods so our first task is to actually throw Unsupported Operation for them. The second step is to define the different enums with the values they are actually representing. The third step is achieved through a static method that converts any Enumeration into our CustomEnumMap. How the map is later used you can see for yourself.
There is one place for improvement though and it is the implementation of the static method. Unfortunately I am just learning java 8 lambdas so I was not able to implement it fast in a good way. But I will work on that and will give you the final implementation of this method later. Or is someone wants to help me out with it is welcome.
public static class CustomEnumMap<K extends Enum<K>,V> extends EnumMap<K, V> {
public CustomEnumMap(EnumMap<K, ? extends V> m) {
super(m);
}
#Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
}
public static enum EnumA {
FIRST("value1"),SECOND("value2"),THREE("value3");
private String value;
private EnumA(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
public static enum EnumB {
FIRST("value1"),SECOND("value2");
private String value;
private EnumB(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
public static <T extends Enum<T>> CustomEnumMap<T, String> toMap(T[] myenum) {
return new CustomEnumMap<T,String>(new EnumMap<T,String>( Arrays.stream(myenum).collect(Collectors.toMap(t->(T)t, t->t.toString()))));
}
public static void main(String args[]) {
CustomEnumMap<EnumA, String> enumA = toMap(EnumA.values());
CustomEnumMap<EnumA, String> enumB = toMap(EnumA.values());
for (String stringA : enumA.values()) {
System.out.print(stringA);
}
System.out.println("");
for (String stringB : enumB.values()) {
System.out.print(stringB);
}
}

Storing specific data types in hashmap

I have to use a map which stores keys of type Integer, String and Long only.
One solution: To store type Object and in put method check with instanceof operator. Is there any better solution, maybe with enum
You can use a map and storing Long as String into it
or you can use two different hashmap and duplicate put/get methods. If you have two types, it is probably for two different things, and having two different map should probably be the correct answer
Create a class that has a map as a member and add methods that will store and retrieve int and long as Strings.
class MyMap {
private Map mabObject = Map<String, Object>;
public void add(long key, Object value) {
mapObject.put(Long.toString(key),value);
}
public void add(String key, Object value) {
mapObject.put(key, value);
}
public Object get(long key) {
return mapObject.get(Long.toString(key));
}
public Object get(String key) {
return mapObject.get(key);
}
}
I agree with Paul Boddington's comment, and the need of such trick shows that code smells.
Just for a funny excercise (not for production code) I've made an example that shows what we can do in compile time for limiting types of keys in a map.
For example we can create a wrapper allowing only values of specific classes.
common/map/Wrap.java
package common.map;
import java.util.Arrays;
import java.util.List;
public class Wrap<T> {
private T value;
private Wrap(T value){
this.value = value;
}
public T get() {
return this.value;
}
/*
* it's important to implement this method
* if we intend to use Wrap instances as map's key
*
* and it's needed to see that hash codes are computing differently in different classes,
* and depending on `allowedClasses` contents we can face some unexpected collisions
* so if you care of performance - test your maps usage accurately
*/
public int hashCode() {
return this.value.hashCode();
}
/*
* static
*/
private static List<Class> allowedClasses = Arrays.asList(Long.class, String.class);
public static <T> Wrap<T> create(Class<? extends T> clazz, T value) {
if (!allowedClasses.contains(clazz)) {
throw new IllegalArgumentException("Unexpected class " + clazz);
}
return new Wrap<>(value);
}
public static <T> Wrap<T> create(AllowedClasses allowedClass, T value) {
return create(allowedClass.clazz, value);
}
public enum AllowedClasses {
LONG(Long.class),
STRING(String.class);
private Class clazz;
AllowedClasses(Class clazz) {
this.clazz = clazz;
}
}
}
And let's run it
common/map/Example.java
package common.map;
import common.map.Wrap.AllowedClasses;
import java.util.HashMap;
import java.util.Map;
public class Example {
public static void main(String... args) {
Map<Wrap, Object> map = new HashMap<>();
// next two lines create wrappers for values of types we added to enum AllowedClasses
// but since enums cannot have type parameters, we are not able to check
// if the second parameter type is compatible with a type associated with given enum value
// so I think usage of enum is useless for your purpose
Wrap<?> valLong0 = Wrap.create(AllowedClasses.LONG, "the string in place of Long is OK");
Wrap<?> valString0 = Wrap.create(AllowedClasses.STRING, 12345);
// from the next lines you can see how we can use the Wrap class to keep
// only allowed types to be associated with the map keys
Wrap<Long> valLong = Wrap.create(Long.class, 1L); // legal
Wrap<String> valString = Wrap.create(String.class, "abc"); // legal
Wrap<String> valWrong = Wrap.create(String.class, 123); // doesn't compile
Wrap<Object> valWrong2 = Wrap.create(Object.class, 123); // compiles but throws exception in runtime
Object obj = ThirdParty.getObjectOfUnknownClass();
Wrap<?> valDynamic = Wrap.create(obj.getClass(), obj); // compiles but MAYBE throws exception in runtime
// so we get to this point only if all the wrappers are legal,
// and we can add them as keys to the map
map.put(valLong, new Object());
map.put(valString, new Object());
map.put(valDynamic, new Object());
}
}
HashMap<DataType1,DataType2>hm = new HashMap<DataType1,DataType2>();
or
Map<DataType1,DataType2> m = new HashMap<DataType1,DataType2>();
m.put(key, value);
Instead of DataType1 & DataType2 you can add Integer,String,Long ,etc. and use the put(key,value) method to enter key and values into the HashMap.

How to model a medium-sized data set with multiple types as a class

Problem
I don't know the best way to model my data. I'm worried my current approach has gotten overly complex, and I want to correct it now before I base any more code off it.
Data to be Modeled
I have data sets that consist of 50+ different data items. Each item consists of:
a unique identifier int
a label String.
validation criteria (min, max, legal characters, etc...).
a value Float, Long, Integer, String, or Date.
The label and validation criteria for each item is the same in every data set. Only the values are dynamic. Order is not important.
Needed Usage Examples
Add data to the data set
dataSet.put(itemIdentifier, value);
Traverse and validate all non-null values in the data set
for (DataItem item : dataSet.values()) {
boolean valid = item.validate();
if (valid) {...}
}
Show the specified items in the given data sets
public void displayData(List<DataSet> dataSets, int... itemsIdentifiers) {...}
Implementation Attempt
My current implementation has an abstract Key class as the "key" to a map. Each type subclasses for its own validation needs. Then, inside the DataSet class, I have public static keys for each item.
abstract public class Key {
public int mId;
public String mLabel;
public Key(int id, String label) {...}
abstract public boolean validate(Object Value);
}
public class FloatKey extends Key {
private int mMin, mMax;
public Key(int id, String label, int min, int max) {...}
public boolean validate(Object Value) {...}
}
// one for each type
...
public class DataSet {
public static Key ITEM_A = new FloatKey(1, "item A", 0, 100);
public static Key ITEM_B = new DateKey(2, "item B", "January 1, 1990");
// ~50 more of these
private Map<Key, Object> mMap;
public void put(int itemId, Object value) {...}
public Set<Object> values() {...};
...
}
I don't like that when I pull values out of DataSet, I need to hold onto the value AND the key so I can do things like DataSet.ITEM_A.validate(someFloat). I also find myself using instanceof and casting frequently when I traverse objects in a set because I need to call subclass-only methods in some situations.
Edits for further clarification
Data items and their validation criteria will require occasional changes and so maintenance should be relatively easy / painless.
Although I could use the Key objects themselves as keys into the map, I will sometimes need to put these keys in a Bundle (part of the android API). I would rather use the label or id (in case labels are the same) to avoid making my Key class Parcelable.
What about this approach:
Create this interface:
interface Validable {
boolean isValid();
}
Then, all data items inherit the following class and implicitly the interface ::
abstract class DataItem implements Validable {
public DataItem(int id, String label, int min, int max) {
}
}
Configure each specific instance of DataItem via constructor parameters, passing the common and the distinct values:
class FloatItem extends DataItem {
public FloatItem(int id, String label, int min, int max, Float value) {
super(id, label, min, max);
// set the Float value here
}
#Override
public boolean isValid() {
// validate here
return true;
}
}
class DateItem extends DataItem {
public DateItem(int id, String label, int min, int max, Date value) {
super(id, label, min, max);
}
#Override
public boolean isValid() {
// validate here
return true;
}
}
The client code would assemble the objects like this::
List<Validable> items = Lists.<Validable>newArrayList(new FloatItem(0, "", 0, 0, Float.NaN),
new DateItem(0, "", 0, 0, new Date()));
(note the usage of Google Guava)
Calling code only needs to do this::
for (Validable validable : items) {
System.out.println(validable.isValid());
}
Please note that this approach requires you to first create 'target' objects, and then ask the question if they are valid. In other words, you are passing the valid-able parameters via constructor and then, you ask the object if it is valid. The object itself will answer the question using the validation criteria inside it...
I hope I understood your problem correctly.
I don't quite understand your goals with the design, so maybe not all of this is correct or directly useful to you, but it's some ideas to play with.
First I'd point out that there are lots of fields in the code you've shown that should be marked final. For example, Key.mId, Key.mLabel, FloatKey.mMin, FloatKey.mMax, all the DataSet.ITEM_X, and DataSet.mMap. Marking them final (1) conveys the intended behavior better, (2) prevents accidents where something like a Key's mId changes, and (3) might have marginal performance benefits.
I wonder why you need the numeric ID for each key/field? If they're required for interfacing with some external application or storage format which already defines those IDs, that makes sense, but if it's only for internal things like this method:
public void displayData(List<DataSet> dataSets, int... itemsIdentifiers) {...}
then that could be more meaningfully implemented using a list of String labels or Key objects, instead of the numeric IDs. Likewise, DataSet.put could possibly use the Key or label instead of the ID.
I find myself using instanceof and casting frequently when I traverse objects in a set
Making Key generic can eliminate some casts. (Well, they will still be present in the bytecode, but not in the source because the compiler will take care of it.) E.g.,
abstract public class Key<T> {
...
abstract public boolean validate(T Value);
}
public class FloatKey extends Key<Float> {
...
public boolean validate(Float value) { ... }
}
In the validate method, you thus avoid the need to cast value.
Also, I'm guessing you currently have a method on class DataSet like this:
public Object get(int itemId) { ... }
If you use the Key instead of numeric ID to retrieve values, and make the method generic, you'll often be able to avoid the need for callers to cast the return value (though the cast is still present inside the get method):
public <T> T get(Key<T> key) { ... }
I don't like that when I pull values out of DataSet, I need to hold onto the value AND the key so I can do things like DataSet.ITEM_A.validate(someFloat).
You could make a class for the value instead of the key. E.g.,
abstract public class Value<T> {
public final int id;
public final String label;
protected Value(int id, String label) {
this.id = id;
this.label = label;
}
abstract public T get();
abstract public void set(T value);
}
public class FloatValue extends Value<Float> {
private final float min, max;
private float value;
public FloatValue(int id, String label, float min, float max, float value) {
super(id, label);
this.min = min;
this.max = max;
set(value);
}
public Float get() { return value; }
public void set(Float value) {
if (value < min | value > max) throw new IllegalArgumentException();
this.value = value;
}
}
public class DataSet {
public final FloatValue itemA = new FloatValue(1, "item A", 0, 100, 0);
...
}
That solves the stated problem, and also eliminates the map lookup previously required on every get/set of a value. However it has the side effect of duplicating the storage for the labels and numeric IDs, as the Value classes are not static fields any more.
In this scenario, to access DataSet values by label (or ID?), you can use reflection to build a map. In class DataSet:
private final Map<String, Value<?>> labelMap = new HashMap<>();
{
for (Field f : DataSet.class.getFields()) {
if (Value.class.isAssignableFrom(f.getType())) {
Value<?> v;
try {
v = (Value<?>)f.get(this);
} catch (IllegalAccessException | IllegalArgumentException e) {
throw new AssertionError(e); // shouldn't happen
}
labelMap.put(v.label, v);
}
}
}
There's a subtlety here: if you subclass DataSet to represent different types of data, then the Value fields of the subclasses will not have been initialized yet at the time DataSet's initializer builds the map. So if you create subclasses of DataSet, you might need a protected init() method to be called from subclass constructors, to tell it to (re)build the map, which is a bit ugly but it would work.
You can re-use this map to provide convenient iteration of a DataSet's values:
public Collection<Value<?>> values() {
return Collections.unmodifiableCollection(labelMap.values());
}
A final idea: if you're using reflection anyway, it might be possible to use ordinary fields for the values, with annotation interfaces to implement their behavior.
import java.lang.annotation.*;
import java.lang.reflect.*;
public class DataSet {
#Label("item A") #ValidateFloat(min=0, max=100) public float itemA;
#Label("item B") public String itemB;
#Retention(RetentionPolicy.RUNTIME)
public static #interface Label {
String value();
}
#Retention(RetentionPolicy.RUNTIME)
public static #interface ValidateFloat {
float min();
float max();
}
public final class Value {
public final String label;
private final Field field;
protected Value(String label, Field field) {
this.label = label;
this.field = field;
}
public Object get() {
try {
return field.get(DataSet.this);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new AssertionError(e); // shouldn't happen
}
}
public void set(Object value) {
try {
field.set(DataSet.this, value);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new AssertionError(e); // shouldn't happen
}
}
public void validate() {
Object value = get();
// Test for presence of each validation rule and implement its logic.
// Ugly but not sure how best to improve this...
if (field.isAnnotationPresent(ValidateFloat.class)) {
float floatValue = (float)value;
ValidateFloat rule = field.getAnnotation(ValidateFloat.class);
if (floatValue < rule.min() || floatValue > rule.max()) {
//throw new Whatever();
}
}
//if (field.isAnnotationPresent(...)) {
// ...
//}
}
}
private final Map<String, Value> labelMap = new HashMap<>();
{
for (Field f : DataSet.class.getFields()) {
if (f.isAnnotationPresent(Label.class)) {
Value value = new Value(f.getAnnotation(Label.class).value(), f);
labelMap.put(value.label, value);
}
}
}
public Collection<Value> values() {
return Collections.unmodifiableCollection(labelMap.values());
}
}
This approach has different tradeoffs. Code that knows exactly what field it wants can access it directly. E.g., dataSet.itemA instead of dataSet.get(DataSet.ITEM_A). Code that needs to iterate multiple fields does so via the Value wrapper (would Property be a better class name? Or Item?), which encapsulates the ugliness of the field reflection code.
I also put the validation logic into the annotations. If there are lots of fields with very simple numeric limits, that works well. If it's too complex for that you'd be better off with a DataSet.validate method that accesses the fields directly. E.g,
public void validate() {
if (itemC < 10 || itemC > itemD) ...
}
Okay, one more idea:
public class DataSet {
public float itemA;
public String itemB;
public static abstract class Value<T> {
public final String label;
protected Value(String label) {
this.label = label;
}
public abstract T get();
public abstract void set(T value);
}
public Value<?>[] values() {
return new Value[] {
new Value<Float>("itemA") {
public Float get() {
return itemA;
}
public void set(Float value) {
itemA = value;
}
},
new Value<String>("itemB") {
public String get() {
return itemB;
}
public void set(String value) {
itemB = value;
}
},
};
}
}
This is simple (no annotations or reflection) but it's repetitive. Since you have "50+" fields, the repetitiveness is probably not ideal as it's easy when copy-pasting to slip up at some point, forgetting to replace itemX = value with itemY = value, but if you only need to write it once it might be acceptable. Validation code could go either on the Value class or the DataSet class.

Is there a Union in Java Generics?

Can I contain two different types in a collection? For example, can I have List< String U Integer > ?
Short answer? No. You can (of course) have a List of Objects, but then you can put anything in it, not just String or Integer objects.
You could create a list of container objects, and that container object would contain either an Integer or String (perhaps via generics). A little more hassle.
public class Contained<T> {
T getContained();
}
and implement Contained<Integer> and Contained<String>.
Of course, the real question is why you want to do this? I would expect a collection to contain objects of the same type, and then I can iterate through and perform actions on these objects without worrying what they are. Perhaps your object hierarchy needs further thought?
Nope. You have a couple of alternatives, though:
You can use a List < Object > and stash whatever you like; or
You can use a List < Class-with-2-members > and put your data in one of those class members.
EDIT: Example.
class UnionHolder {
public String stringValue;
public int intValue;
}
List < UnionHolder > myList
...
Of course you'll need a bit of additional code to figure out which kind of data to pull out of the UnionHolder object you just got out of your list. One possibility would be to have a 3rd member which has different values depending on which it is, or you could, say, have a member function like
public boolean isItAString() { return (this.stringValue != null }
If you are doing something like functional programming in Java 8 or above, you may want to try JavaSealedUnions:
Union2.Factory<String, Integer> factory = GenericUnions.doubletFactory();
Union2<String, Integer> strElem = factory.first("hello");
Union2<String, Integer> intElem = factory.second(3);
List<Union2<String, Integer>> list = Array.asList(strElem, intElem);
for (Union2<String, Integer> elem : list) {
elem.continued(
strElem -> System.out.println("string: " + strElem),
intElem -> System.out.println("integer: " + intElem));
}
Haven't tested this, but I think you got the idea.
In addition to the nice answers already provided ...
Possibly, you have the two data types in your algorithm. But you may not have to put them in the same list...
Creating two typed lists could be the clearer for your algorithm, you would still keep the "type-safeness" and carry all your data. Two code samples follow, the second grouping the two lists in a MyData object.
public class Algorithm1 {
public void process(List<String> strings, List<Integer> integers) {
...
}
}
--------------------------------------
public class DataPair {
public List<String> strings;
public List<Integer> integers;
}
public class Algorithm2 {
public void process(DataPair dataPair) {
...
}
}
what you're decribing is the perfect use case for the Visitor pattern
100% statically type-checked
doesn't need Java 8 or above
usage:
List<UnionType> unionTypes = Arrays
.asList(new StringContainer("hello"), new IntegerContainer(4));
for (UnionType unionType : unionTypes) {
unionType.when(new UnionType.Cases<Integer>() {
#Override
public Integer is(StringContainer stringContainer) {
// type-specific handling code
}
#Override
public Integer is(IntegerContainer integerContainer) {
// type-specific handling code
}
});
}
boilerplate code:
interface UnionType {
<R> R when(Cases<R> c);
interface Cases<R> {
R is(StringContainer stringContainer);
R is(IntegerContainer integerContainer);
}
}
class StringContainer implements UnionType {
private final String value;
public StringContainer(String value) { this.value = value; }
public String getValue() { return value; }
#Override
public <R> R when(Cases<R> cases) {
return cases.is(this);
}
}
class IntegerContainer implements UnionType {
private final Integer value;
public IntegerContainer(Integer value) { this.value = value; }
public Integer getValue() { return value; }
#Override
public <R> R when(Cases<R> cases) {
return cases.is(this);
}
}
No. Think about it this way: with generics, the whole idea is to provide type safety. That would not be possible if you could put Objects of different types into it.
You can use the non-generic java.util.List for your purpose.
If you want to ensure that only String or Integer objects enter the list, you could create your own List implementation like so:
public class MySpecialList {
private List list= new LinkedList();
...
public void add(final String string) {
list.add(string);
}
public void add(final Integer integer) {
list.add(integer);
}
...
// add rest of List style methods
}
Drawback: you loose the List interface clarity...

Categories