Related
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.
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.
I could workaround this problem but I cannot understand it, so I am asking for some explanation (and maybe a better question title as well).
Please consider this:
public class TBGService {
// TBGObject is an abstract base class which is extended by model classes
public <T> T doGet(TBGObject model) throws TBGServiceException {
String uri = model.buildUrl(repository) + model.getObjectKey();
GetMethod method = new GetMethod(uri);
T returned = execute(method, credentials, model.getClass());
return returned;
}
}
and this:
public enum TBGTaskAttributes {
private TBGTaskAttributes(String id, String type, String label, Object... flags) {
builder = new TaskAttributeBuilder();
builder.withId(id).withLabel(label);
for (Object flag : flags) {
processFlag(flag);
}
}
public abstract String getValueFromIssue(TBGIssue issue);
public abstract void setValueInIssue(TBGIssue issue, String value);
}
when I write this code to define an enum item:
PROJECT(TaskAttribute.PRODUCT, TaskAttribute.TYPE_SINGLE_SELECT, "Project", new OptionProvider() {
#Override
public Set<Entry<String, String>> getOptions(TaskRepository repository) {
try {
List<TBGProject> list = TBGService.get(repository)
.doGet(new TBGProjects()).getProjects();
[...]
return map.entrySet();
} catch (TBGServiceException e) { [...] }
return null;
}
}) {
#Override
public String getValueFromIssue(TBGIssue issue) {
return issue.getProjectKey();
}
#Override
public void setValueInIssue(TBGIssue issue, String value) {
issue.setProjectKey(value);
}
},
[... other items ...]
I get compiler error (also eclipse auto-completion does not work):
The method getProjects() is undefined for the type Object
and if I hover the doGet method, eclipse show it as defined like:
<Object> Object TBGService.doGet(TBGObject model)
Elsewhere, hovering shows the signature correctly as:
<TBGProjects> TBGProjects TBGService.doGet(TBGObject model)
when called with parameter new TBGProjects().
Just changing:
List<TBGProject> list = TBGService.get(repository)
.doGet(new TBGProjects()).getProjects();
with:
TBGProjects projects = TBGService.get(repository).doGet(new TBGProjects());
List<TBGProject> = projects.getProjects();
makes it work. But what's happening here? What am I missing?
Java infers the type of T based on what you assign the return value of the method to.
If you don't assign the return value to anything, Java has no idea what T should be.
To fix this, you can change the parameter to be of type T so Java can infer T from the parameter you pass:
public <T extends TBGObject> T doGet(T model) throws TBGServiceException {
This question already has answers here:
How to return multiple objects from a Java method?
(25 answers)
Closed 9 years ago.
I'm trying to do something like this:
public void <String,int> getItem
{
return <"Jen",23>;
}
I know I can use a custom class, but how I would I return two results in one function call.
1 - Is the above template function possible in java, and how would by caller get part 1 of it and part 2 later.
2 - Can I do it using an associative array like in actionscript?
3 - Can I do it using a hashmap of some sort?
4 - What are other possible ways are there
I attempted all three ways, but one way or another syntax is hitting me. So if anyone can give clear examples
Java functions always return a single value, so your only option is to return a "collection" object which contains multiple values, such as an Array or a proper Collection. For example:
public Object[] getItem() { return new Object[] { "Jen", 23 }; }
public Collection<Object> { return Arrays.asList("Jen", 23); }
Although, a typical pattern in Java is to return a custom type which encapsulates your values, e.g.:
public class NameAge {
public final String name;
public final int age;
public NameAge(String name, int age) {
this.name = name;
this.age = age;
}
}
// ...
public NameAge getItem() { return new NameAge("Jen", 23); }
Or more generally:
public class Pair<X, Y> {
public final X x;
public final Y y;
public Pair(X x, Y y) {
this.x = x;
this.y = y;
}
}
// ...
public Pair<String,Integer> getItem() {
return new Pair<String,Integer>("Jen", 23);
}
Of course, there are serious implications regarding hashing (equality and hash code) if you want to use these custom types as hash keys.
I like using generics! Create your own class and return an instance of it:
public class Tuple<T,V>
{
public T item1;
public V item2;
public Tuple(T i1, V i2)
{
item1 = i1;
item2 = i2;
}
}
Then you create your method:
public Tuple<String, int> getItem()
{
return new Tuple<String, int>("Jen", 23);
}
Java does not allow for multiple return statements. The best practice I believe is to create a custom object. What you have here suggests some sort of Person class, a la
public class Person {
int Age;
String Name;
}
Returning an object will make it more intuitive what you are doing as well.
You can return a Bundle.
public Bundle getItem(){
Bundle theBundle = new Bundle();
theBundle.putString("name","Jen");
theBundle.putInt("age",23);
return theBundle;
}
Usually, if you need to return two values from one function - it's a code smell. Try to refactor your code so that every function always return just one value. Keep in mind that no return value (void) is also a code smell, but less critical.
The proper way would be to create a class for your return set:
public class ReturnSet {
private String str;
private int num;
public ReturnSet(String _str, int _num) {
str = _str;
num = _num;
}
//add getters and setters
...
}
Then your function would look like
public ReturnSet getItem() {
...
return new ReturnSet(strValue, intValue);
}
Of course, you can fudge things by having your function return an array of Object, but this would be a rather bad code:
public Object[] getItem() {
Object[] result;
//allocate it, get data;
...
result[1] = strValue;
relult[2] = new Integer(intValue);
return result;
}
You can even return a hashmap with one element in it:
public Map getItem() {
Map result;
//allocate it, say as hashmap, get data;
...
result.put(strValue, new Integer(intValue));
return result;
}
Then in the caller, the key of the map would be the first part and the value would be the second.
While there are may be many ways of doing things like that, the first one is the right approach.
If a method returns something, then its return type must be this something:
public MyCustomObject getItem();
or
public Object[] getItem():
or anything else wher you can store the results.
But Java is a statically typed OO language. A custom class is the way to go.
You can also return one value the regular way and other(s) by using a "return" parameter:
class C {
Type2 value; // omitted getter and setter for brevity
}
Type1 f1(C returnParameter, String otherParameters...)
{
// function body here
returnParameter.value=returnValue2; // store the second result
return returnValue1; // return the first result
}
// usage
Type1 result1;
Type2 result2;
C helper = new C();
result1=f1(helper, "foo", "bar");
result2=helper.value;
For more results either use several "helper" objects or one that can hold several values.
I am myself looking for a most elegant solution (in my case one return type is a Collection and the other is an integer number-any variant of it is OK).
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...