Related
I want to write a class with more than 1 fields of different types but at any time, there is one and only one field of an instance object having non null value.
What I did so far does not look really clean.
class ExclusiveField {
private BigInteger numericParam;
private String stringParam;
private LocalDateTime dateParam;
public void setNumericParam(BigInteger numericParam) {
unsetAll();
this.numericParam = Objects.requireNonNull(numericParam);
}
public void setStringParam(String stringParam) {
unsetAll();
this.stringParam = Objects.requireNonNull(stringParam);
}
public void setDateParam(LocalDateTime dateParam) {
unsetAll();
this.dateParam = Objects.requireNonNull(dateParam);
}
private void unsetAll() {
this.numericParam = null;
this.stringParam = null;
this.dateParam = null;
}
}
Does Java support this pattern somehow or is there a more decent way to do it?
The simplest approach for an object to have only one non-null field, is to actually have only one field and assume all others to be null implicitly. You only need another tag field, to determine which field is non-null.
Since in your example, all alternatives seem to be about the type of the value, the type itself could be the tag value, e.g.
class ExclusiveField {
private Class<?> type;
private Object value;
private <T> void set(Class<T> t, T v) {
value = Objects.requireNonNull(v);
type = t;
}
private <T> T get(Class<T> t) {
return type == t? t.cast(value): null;
}
public void setNumericParam(BigInteger numericParam) {
set(BigInteger.class, numericParam);
}
public BigInteger getNumericParam() {
return get(BigInteger.class);
}
public void setStringParam(String stringParam) {
set(String.class, stringParam);
}
public String getStringParam() {
return get(String.class);
}
public void setDateParam(LocalDateTime dateParam) {
set(LocalDateTime.class, dateParam);
}
public LocalDateTime getDateParam() {
return get(LocalDateTime.class);
}
}
If the type is not the only differentiator, you need to define distinct key values. An enum would be a natural choice, but unfortunately, enum constants can not provide the type safety. So, the alternative would look like:
class ExclusiveField {
private static final class Key<T> {
static final Key<String> STRING_PROPERTY_1 = new Key<>();
static final Key<String> STRING_PROPERTY_2 = new Key<>();
static final Key<BigInteger> BIGINT_PROPERTY = new Key<>();
static final Key<LocalDateTime> DATE_PROPERTY = new Key<>();
}
private Key<?> type;
private Object value;
private <T> void set(Key<T> t, T v) {
value = Objects.requireNonNull(v);
type = t;
}
#SuppressWarnings("unchecked") // works if only set() and get() are used
private <T> T get(Key<T> t) {
return type == t? (T)value: null;
}
public void setNumericParam(BigInteger numericParam) {
set(Key.BIGINT_PROPERTY, numericParam);
}
public BigInteger getNumericParam() {
return get(Key.BIGINT_PROPERTY);
}
public void setString1Param(String stringParam) {
set(Key.STRING_PROPERTY_1, stringParam);
}
public String getString1Param() {
return get(Key.STRING_PROPERTY_1);
}
public void setString2Param(String stringParam) {
set(Key.STRING_PROPERTY_2, stringParam);
}
public String getString2Param() {
return get(Key.STRING_PROPERTY_2);
}
public void setDateParam(LocalDateTime dateParam) {
set(Key.DATE_PROPERTY, dateParam);
}
public LocalDateTime getDateParam() {
return get(Key.DATE_PROPERTY);
}
}
Change your unsetAll method to setAll:
private void setAll(BigInteger numericParam, String stringParam, LocalDateTime dateParam) {
this.numericParam = numericParam;
this.stringParam = stringParam;
this.dateParam = dateParam;
}
Then invoke from your public setters like:
public void setNumericParam(BigInteger numericParam) {
setAll(Objects.requireNonNull(numericParam), null, null);
}
Note that Objects.requireNonNull is evaluated before setAll, so if you were to pass in a null numericParam, this would fail without changing any internal state.
preface: My answer is more theoretical, and the practices it describes aren't really practical in Java. They're simply not as well supported, and you would be "going against the grain", conventionally speaking. Regardless, I think it's a neat pattern to know about, and I thought I would share.
Java's classes are product types. When a class C contains members of types T1, T2, ..., Tn, then the valid values for objects of class C are the Cartesian product of the values of T1, T2, ..., Tn. For example, if class C contains a bool (which has 2 values) and byte (which has 256 values), then there are 512 possible values of C objects:
(false, -128)
(false, -127)
...
(false, 0)
...
(false, 127)
(true, -128)
(true, -127)
...
(true, 0)
...
(true, 127)
In your example, the theoretical possible values of ExclusiveField is equal to numberOfValuesOf(BigInteger.class) * numberOfValuesOf(String) * numberOfValuesOf(LocalDateTime) (notice the multiplication, that's why it's called a product type), but that's not really what you want. You're looking for ways to eliminate a huge set of these combinations so that the only values are when one field is non-null, and the others are null. There are numberOfValuesOf(BigInteger.class) + numberOfValuesOf(String) + numberOfValuesOf(LocalDateTime). Notice the addition, this indicates that what you're looking for is a "sum type".
Formally speaking, what you're looking for here is a tagged union (also called a variant, variant record, choice type, discriminated union, disjoint union, or sum type). A tagged union is a type whose values are a choice between one value of the members. In the previous example, if C was a sum type, there would be only 258 possible values: -128, -127, ..., 0, 127, true, false.
I recommend you check out unions in C, to build an understanding of how this works. The issue with C is that its unions had no way of "remembering" which "case" was active at any given point, which mostly defeats the whole purpose of a "sum type". To remedy this, you would add a "tag", which was an enum, whose value tells you what the state of the union is. "Union" stores the payload, and the "tag" tells you to the type of the payload, hence "tagged union".
The problem is, Java doesn't really have such a feature built in. Luckily, we can harness class hierarchies (or interfaces) to implement this. You essentially have to roll your own every time you need it, which is a pain because it takes a lot of boilerplate, but it's conceptually simple:
For n different cases, you make n different private classes, each storing the members pertinent to that case
You unify these private classes under a common base class (typically abstract) or interface
You wrap these classes in a forwarding class that exposes a public API all while hiding the private internals (to ensure that no one else can implement your interface).
Your interface could have n methods, each something like getXYZValue(). These methods could be made as default methods, where the default implementation returns null (for Object values, but doesn't work for primitives, Optional.empty() (for Optional<T> values), or throw an exception (gross, but there's no better way for primitive values like int). I don't like this approach, because the interface is rather disingenuous. Conforming types don't really conform to the interface, only ยน/n th of it.
Instead, you can use a pattern matching uhhh, pattern. You make a method (e.g. match) that takes n different Function parameters, whose types correspond to the types of cases of the discriminated union. To use a value of the discriminated union, you match it and provide n lambda expressions, each of which acts like the cases in a switch statement. When invoked, the dynamic dispatch system calls the match implementation associated with the particular storage object, which calls the correct one of the n functions and passes its value.
Here's an example:
import java.util.Optional;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Consumer;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.math.BigInteger;
class Untitled {
public static void main(String[] args) {
List<ExclusiveField> exclusiveFields = Arrays.asList(
ExclusiveField.withBigIntegerValue(BigInteger.ONE),
ExclusiveField.withDateValue(LocalDateTime.now()),
ExclusiveField.withStringValue("ABC")
);
for (ExclusiveField field : exclusiveFields) {
field.consume(
i -> System.out.println("Value was a BigInteger: " + i),
d -> System.out.println("Value was a LocalDateTime: " + d),
s -> System.out.println("Value was a String: " + s)
);
}
}
}
class ExclusiveField {
private ExclusiveFieldStorage storage;
private ExclusiveField(ExclusiveFieldStorage storage) { this.storage = storage; }
public static ExclusiveField withBigIntegerValue(BigInteger i) { return new ExclusiveField(new BigIntegerStorage(i)); }
public static ExclusiveField withDateValue(LocalDateTime d) { return new ExclusiveField(new DateStorage(d)); }
public static ExclusiveField withStringValue(String s) { return new ExclusiveField(new StringStorage(s)); }
private <T> Function<T, Void> consumerToVoidReturningFunction(Consumer<T> consumer) {
return arg -> {
consumer.accept(arg);
return null;
};
}
// This just consumes the value, without returning any results (such as for printing)
public void consume(
Consumer<BigInteger> bigIntegerMatcher,
Consumer<LocalDateTime> dateMatcher,
Consumer<String> stringMatcher
) {
this.storage.match(
consumerToVoidReturningFunction(bigIntegerMatcher),
consumerToVoidReturningFunction(dateMatcher),
consumerToVoidReturningFunction(stringMatcher)
);
}
// Transform 'this' according to one of the lambdas, resuling in an 'R'.
public <R> R map(
Function<BigInteger, R> bigIntegerMatcher,
Function<LocalDateTime, R> dateMatcher,
Function<String, R> stringMatcher
) {
return this.storage.match(bigIntegerMatcher, dateMatcher, stringMatcher);
}
private interface ExclusiveFieldStorage {
public <R> R match(
Function<BigInteger, R> bigIntegerMatcher,
Function<LocalDateTime, R> dateMatcher,
Function<String, R> stringMatcher
);
}
private static class BigIntegerStorage implements ExclusiveFieldStorage {
private BigInteger bigIntegerValue;
BigIntegerStorage(BigInteger bigIntegerValue) { this.bigIntegerValue = bigIntegerValue; }
public <R> R match(
Function<BigInteger, R> bigIntegerMatcher,
Function<LocalDateTime, R> dateMatcher,
Function<String, R> stringMatcher
) {
return bigIntegerMatcher.apply(this.bigIntegerValue);
}
}
private static class DateStorage implements ExclusiveFieldStorage {
private LocalDateTime dateValue;
DateStorage(LocalDateTime dateValue) { this.dateValue = dateValue; }
public <R> R match(
Function<BigInteger, R> bigIntegerMatcher,
Function<LocalDateTime, R> dateMatcher,
Function<String, R> stringMatcher
) {
return dateMatcher.apply(this.dateValue);
}
}
private static class StringStorage implements ExclusiveFieldStorage {
private String stringValue;
StringStorage(String stringValue) { this.stringValue = stringValue; }
public <R> R match(
Function<BigInteger, R> bigIntegerMatcher,
Function<LocalDateTime, R> dateMatcher,
Function<String, R> stringMatcher
) {
return stringMatcher.apply(this.stringValue);
}
}
}
Why not simply?
public void setNumericParam(BigInteger numericParam) {
this.numericParam = Objects.requireNonNull(numericParam);
this.stringParam = null;
this.dateParam = null;
}
Your goal
You mention in the comments that your goal is to write SQL requests for a legacy DB:
type:VARCHAR, numeric: INT, string: VARCHAR, date: DATETIME and
ExclusiveField will be used as getQueryRunner().query("CALL
sp_insert_parametter(?, ?, ?, ?, ?)", param.getNumericParam(), id,
type, param.getStringParam(), param.getDateParam())
So your goal really isn't to create a class with only one non-null field.
Alternative
You could define an abstract class Field with id, type, value attributes:
public abstract class Field
{
private int id;
private Class<?> type;
private Object value;
public Field(int id, Object value) {
this.id = id;
this.type = value.getClass();
this.value = value;
}
public abstract int getPosition();
}
For each column in your database, you create a small corresponding class, extending Field. Each class defines its desired type and its position in the SQL command:
import java.math.BigInteger;
public class BigIntegerField extends Field
{
public BigIntegerField(int id, BigInteger numericParam) {
super(id, numericParam);
}
#Override
public int getPosition() {
return 0;
}
}
You can define Field#toSQL:
public String toSQL(int columnsCount) {
List<String> rows = new ArrayList<>(Collections.nCopies(columnsCount, "NULL"));
rows.set(getPosition(), String.valueOf(value));
return String.format("SOME SQL COMMAND (%d, %s, %s)", id, type.getName(), String.join(", ", rows));
}
Which will output NULLS everywhere except at the desired position.
That's it.
Complete code
Field.java
package com.stackoverflow.legacy_field;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public abstract class Field
{
private int id;
private Class<?> type;
private Object value;
public Field(int id, Object value) {
this.id = id;
this.type = value.getClass();
this.value = value;
}
public abstract int getPosition();
public static void main(String[] args) {
List<Field> fields = Arrays.asList(new BigIntegerField(3, BigInteger.TEN),
new StringField(17, "FooBar"),
new DateTimeField(21, LocalDateTime.now()));
for (Field field : fields) {
System.out.println(field.toSQL(3));
}
}
public String toSQL(int columnsCount) {
List<String> rows = new ArrayList<>(Collections.nCopies(columnsCount, "NULL"));
rows.set(getPosition(), String.valueOf(value));
return String.format("SOME SQL COMMAND (%d, %s, %s)", id, type.getName(), String.join(", ", rows));
}
}
BigIntegerField.java
package com.stackoverflow.legacy_field;
import java.math.BigInteger;
public class BigIntegerField extends Field
{
public BigIntegerField(int id, BigInteger numericParam) {
super(id, numericParam);
}
#Override
public int getPosition() {
return 0;
}
}
StringField.java
package com.stackoverflow.legacy_field;
public class StringField extends Field
{
public StringField(int id, String stringParam) {
super(id, stringParam);
}
#Override
public int getPosition() {
return 1;
}
}
DateTimeField.java
package com.stackoverflow.legacy_field;
import java.time.LocalDateTime;
public class DateTimeField extends Field
{
public DateTimeField(int id, LocalDateTime value) {
super(id, value);
}
#Override
public int getPosition() {
return 2;
}
}
Result
Launching Field#main outputs:
SOME SQL COMMAND (3, java.math.BigInteger, 10, NULL, NULL)
SOME SQL COMMAND (17, java.lang.String, NULL, FooBar, NULL)
SOME SQL COMMAND (21, java.time.LocalDateTime, NULL, NULL, 2019-05-09T09:39:56.062)
Which should be really close to your desired output. You could probably find better names and define specific toString() methods if needed.
You could use reflection. Two functions and you're done. Add a new field? No problem. You don't even have to change anything.
public void SetExclusiveValue(String param, Object val){
this.UnsetAll();
Class cls = this.getClass();
Field fld = cls.getDeclaredField(param); //Maybe need to set accessibility temporarily? Or some other kind of check.
//Also need to add check for fld existence!
fld.set(this, Objects.requireNonNull(val));
}
private void UnsetAll(){
Class cls = this.getClass();
Field[] flds = cls.getDeclaredFields();
for (Field fld : flds){
fld.set(this,null);
}
}
If accessibiility is an issue, you could simply add a list of accessible fields and check param against that
class Value<T> {
T value;
}
I would like to define a method and by passing the enum, returns the mapped type based on the enum. So far I only work out this way:
public class Person {
HashMap<String, Object> mData;
void int getDetail(DetailInt detail){
Object data = mData.get(detail.name());
if(data instanceof Integer)
return (int)data;
return 0;
}
void String getDetail(DetailStr detail){
Object data = mData.get(detail.name());
if(data instanceof String)
return (String)data;
return "";
}
}
public enum DetailInt {
Age("age"), Weight("weight"), Height("height");
String columnName;
DetailInt(String columnName){
this.columnName= columnName;
}
}
public enum DetailStr {
FirstName("first_name"), LastName("last_name");
String columnName;
DetailStr (String columnName){
this.columnName= columnName;
}
}
So I can use the same method, but passing different enums to get the data with the type.
int age = person.getDetail(DetailInt.Age);
String firstName = person.getDetail(DetailStr.FirstName);
Now, what I would like to achieve is to merge both enums together, so I can call as below:
int age = person.getDetail(Detail.Age);
String firstName = person.getDetail(Detail.FirstName);
It is neater. However, I have tried generic type and interface, still cannot find the way to do it. Use below way is similar to what I want but this is not enum type.
abstract class Detail {
}
class DetailStr extend Detail {
}
interface Details {
DetailStr firstName = new DetailStr("first_name");
DetailStr lastName = new DetailStr("las_name");
DetailInt age = new DetailInt("age");
DetailInt weight = new DetailInt("weight");
DetailInt height = new DetailInt("height");
}
public class Person {
void int getDetail(DetailInt detail){
....
}
void String getDetail(DetailStr detail){
....
}
}
You can't do this in Java.
This is because a particular value of an enumerator has the same type as any other value of that enumerator. It's therefore not possible to construct an overloaded function since there's no type difference to act as a descriminator. (You cannot overload a function by return type difference alone.)
The obvious solution is to have two methods getDetailAsInt and getDetailAsString.
I'll share this approach that does not use enums, but it might be of some use to you:
public class Key<T> {
private String key;
...
}
public class Keys {
public static final Key FIRST_NAME = new Key<String>("first_name");
public static final Key AGE = new Key<Integer>("age");
}
public class Person {
public <T> T getDetail(Key<T> key) {
Object detail = mData.get(key.getKey());
return (T) detail;
}
}
I'm afraid it might not be possible to convert it to use enums, so you'd have to ensure no unwanted keys are created in some other way (package-private constructor etc.)
Let's say I have these set of POJO class that implement an interface but there are no common attributes here.
public interface MainIfc {}
class Ifc1 implements MainIfc {
private String a1;
public String getA1() {
return a1;
}
public void setA1(String a1) {
this.a1 = a1;
}
}
class Ifc2 implements MainIfc {
private String x1;
private String x2;
public String getX1() {
return x1;
}
public void setX1(String x1) {
this.x1 = x1;
}
public String getX2() {
return x2;
}
public void setX2(String x2) {
this.x2 = x2;
}
}
And in conjunction with these POJO classes I have a couple of methods which I can use to retrieve the type of POJO being returned based on another value and the actual POJO with values.
public class GetIfc {
public Class getIfcType(int code) {
if (code==1)
return Ifc1.class;
else
return Ifc2.class;
}
public MainIfc getIfc(int code) {
if (code==1) {
Ifc1 thisIfc = new Ifc1();
thisIfc.setA1("Ifc1");
return thisIfc;
} else {
Ifc2 thisIfc = new Ifc2();
thisIfc.setX1("Ifc2");
thisIfc.setX2("Ifc2");
return thisIfc;
}
}
}
Is there a way using which I can read the concrete POJO safely in my code and use the getters/setters? I have gone through quite a few questions which provide answers based on Reflection but that isn't working for me. The getters/setters aren't visible and when I call .getClass() on the returned Object I see it is the MainIfc interface.
The design problem I am trying to solve pertains to a REST API automation framework that I am trying to design. Basically I have a ClientResponse parser which will send back the POJO I am looking for. But I don't want the folks writing the test cases to worry about the type of POJO that is returned. So I was thinking I could return the type and the instantiated POJO so I get the values but I am troubled over how to achieve this dynamically.
Try this code. Maybe this will return all the methods in class as well as methods inherited from Object class.
public static void main(String[] args) throws ClassNotFoundException {
GetIfc getIfc=new GetIfc();
MainIfc clas1s=getIfc.getIfc(1);
Class class1= clas1s.getClass();
System.out.println(class1);
Method[] mem= class1.getMethods();
for(Method mmm : mem) {
System.out.println(mmm.getName());
}
}
Do consumers of MainIfc actually need the POJOs, or just the data inside of them?
It might be cleaner design if MainIfc declares a method or two that exposes the data that its consumers will need. Your POJOs can then implement the methods that the MainIfc interface declares. Or you can build a wrapper class for each POJO that conforms it to the interface, if you want to keep the concerns of implementing your interface separate from your POJOs.
Ideally an interface should expose a few methods that can be used to interact with any class which implements it and no one should need to know about the underlying POJOs/implementation.
public interface MainIfc {
public Hash getAttributes();
public setAttributes(Hash attributes);
}
class Ifc1 implements MainIfc {
private String a1;
public String getA1() {
return a1;
}
public void setA1(String a1) {
this.a1 = a1;
}
public Hash getAttributes() {
// return a hash with data that MainIfc consumer will need from this POJO
}
public setAttributes(Hash attributes) {
// copy hash attributes to corresponding POJO fields
}
}
class Ifc2 implements MainIfc {
private String x1;
private String x2;
public String getX1() {
return x1;
}
public void setX1(String x1) {
this.x1 = x1;
}
public String getX2() {
return x2;
}
public void setX2(String x2) {
this.x2 = x2;
}
public Hash getAttributes() {
// return a hash with data that MainIfc consumer will need from this POJO
}
public setAttributes(Hash attributes) {
// copy hash attributes to corresponding POJO fields
}
}
It sounds to me like you're trying to do something rather illogical. Strategy Pattern or Abstract Factory might be a good fit for your requirement, but at the moment I don't quite understand what exactly it is you're trying to achieve. You should definitely not be conditionally casting and calling different methods on these classes. If you really want to continue on this path, I would suggest going with reflection, if not an option, and you need the flexibility, I'd probably go with a Map of some kind.
But I would definitely rethink your design if at all possible.
Try this piece of code, I don't know if I fully understand your requirement but based on my understanding I think below code would do the trick.
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
GetIfc getIfc = new GetIfc();
MainIfc clas1s = getIfc.getIfc(1);
Field[] fields = clas1s.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
Class fieldClasslasse = field.getType();
if (field.getModifiers() == Modifier.PRIVATE) {
// you need to check fieldClass, if it is boolean then initials of the getter could be 'is' instead of 'get'
String methodNameGet = "get" + Character.toUpperCase(field.getName().charAt(0))
+ field.getName().substring(1);
String methodNameSet = "set" + Character.toUpperCase(field.getName().charAt(0))
+ field.getName().substring(1);
Method methodGet = clas1s.getClass().getDeclaredMethod(methodNameGet, null);
Object value = methodGet.invoke(clas1s, null);
if (value != null && value instanceof String) {
String valueUpper = ((String)value).toUpperCase();
Class[] cArg = new Class[1];
cArg[0] = String.class;
Method methodSet = clas1s.getClass().getDeclaredMethod(methodNameSet, cArg);
Object[] var = new Object[1];
var[0] = valueUpper;
methodSet.invoke((Object) clas1s, var);
}
}
}
}
A little explanation about above code : Get all the fileds of the object and check if is a private property, if yes then it must have a public getter and setter, guess their name based on java convention, call the getter, get the value, check if it is a instance of String class, if yes make it UPPERCASE then call setter to set new value.
Well confusing title, to put it longer but simpler, can you create an object class, in which a constructor will create multiple of the objects of the class and store them in an static object arrayList, all this in one class? If you can't can you make a static void in that class to do so?
Regards
Augustas
I recommend implementing Flyweight Pattern. From wikipedia:
A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory.
Java already implements this in Integer class through the static factory method Integer#valueOf method and the internal IntegerCache class that holds by default Integers from -128 til 127. Here's an example:
Integer i1, i2, i3;
i1 = 100; //it will call Integer with value of 100 from cache
i2 = Integer.valueOf(100); //it will call Integer with value of 100 from cache
i3 = new Integer(100); //creates a new instance
System.out.println(i1 == i2); //true
System.out.println(i1 == i3); //false
System.out.println(i1.equals(i2)); //true
System.out.println(i1.equals(i3)); //true
Knowing this, you can create your own flyweight implementation:
public class YourClass {
private final String name;
private final String description;
//more fields...
//making a private constructor in case you don't want other classes
//to create instance of this class carelessly
//like Integer
private YourClass(String name, String description) {
this.name = name;
this.description = description;
//probably more logic here
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
#Override
public int hashCode() {
//sample implementation
//it can be heavily improved
return name.hashCode();
}
#Override
public boolean equals(Object o) {
//sample implementation
//it MUST be heavily improved
if (o == this) return true;
if (!(o instanceof YourClass)) return false;
YourClass other = (YourClass)o;
return this.name.equals(other.getName());
}
//static flyweight manager
private static class YourClassFlyweight {
//cache with weak entries
//wrapped into a synchronized Map
static final Map<String, YourClass> cache =
Collections.synchronizedMap(
new WeakHashMap<String, YourClass>());
//if you don't want weak entries
//then just use a ConcurrentHashMap
//static final Map<String, YourClass> cache =
// new ConcurrentHashMap<String, YourClass>()));
private YourClassFlyweight() { }
}
//using Factory Method along with this flyweight implementation
public static YourClass create(String name, String description) {
//check if it's not created
if (YourClassFlyweight.cache.containsKey(name)) {
//if it is, then return current instance
return YourClassFlyweight.cache.get(name);
}
//otherwise, create the instance and add it into cache
synchronized(YourClassFlyweight.cache) {
YourClass newInstance = new YourClass(name, description);
YourClassFlyweight.cache.put(name, newInstance);
return newInstance;
}
}
}
Basic test for this:
YourClass ins1 = YourClass.create("Luiggi", "Mendoza");
YourClass ins2 = YourClass.create("Luiggi", "OtherLastName");
System.out.println(ins1.equals(ins2)); // true
System.out.println(ins1 == ins2); // unbelievably, true
Also, instead using Map with some implementation for a cache, you may use a real cache library like ehcache instead.
you could do it like this, i'll leave the why to you, but it is generally a good question to ask youself, why am i doing it this way...
import java.util.ArrayList;
public class Static {
public static ArrayList<Foo> foos = new ArrayList<Foo>();
//this initializes your list of objects...
static
{
foos.add(new Foo("A"));
foos.add(new Foo("B"));
foos.add(new Foo("C"));
foos.add(new Foo("D"));
}
}
class Foo
{
String value;
public Foo(String v) { value = v; }
}
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.