I'm writing a library, which has a predefined set of values for an enum.
Let say, my enum looks as below.
public enum EnumClass {
FIRST("first"),
SECOND("second"),
THIRD("third");
private String httpMethodType;
}
Now the client, who is using this library may need to add few more values. Let say, the client needs to add CUSTOM_FIRST and CUSTOM_SECOND. This is not overwriting any existing values, but makes the enum having 5 values.
After this, I should be able to use something like <? extends EnumClass> to have 5 constant possibilities.
What would be the best approach to achieve this?
You cannot have an enum extend another enum, and you cannot "add" values to an existing enum through inheritance.
However, enums can implement interfaces.
What I would do is have the original enum implement a marker interface (i.e. no method declarations), then your client could create their own enum implementing the same interface.
Then your enum values would be referred to by their common interface.
In order to strenghten the requirements, you could have your interface declare relevant methods, e.g. in your case, something in the lines of public String getHTTPMethodType();.
That would force implementing enums to provide an implementation for that method.
This setting coupled with adequate API documentation should help adding functionality in a relatively controlled way.
Self-contained example (don't mind the lazy names here)
package test;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<HTTPMethodConvertible> blah = new ArrayList<>();
blah.add(LibraryEnum.FIRST);
blah.add(ClientEnum.BLABLABLA);
for (HTTPMethodConvertible element: blah) {
System.out.println(element.getHTTPMethodType());
}
}
static interface HTTPMethodConvertible {
public String getHTTPMethodType();
}
static enum LibraryEnum implements HTTPMethodConvertible {
FIRST("first"),
SECOND("second"),
THIRD("third");
String httpMethodType;
LibraryEnum(String s) {
httpMethodType = s;
}
public String getHTTPMethodType() {
return httpMethodType;
}
}
static enum ClientEnum implements HTTPMethodConvertible {
FOO("GET"),BAR("PUT"),BLAH("OPTIONS"),MEH("DELETE"),BLABLABLA("POST");
String httpMethodType;
ClientEnum(String s){
httpMethodType = s;
}
public String getHTTPMethodType() {
return httpMethodType;
}
}
}
Output
first
POST
Enums are not extensible. To solve your problem simply
turn the enum in a class
create constants for the predefined types
if you want a replacement for Enum.valueOf: track all instances of the class in a static map
For example:
public class MyType {
private static final HashMap<String,MyType> map = new HashMap<>();
private String name;
private String httpMethodType;
// replacement for Enum.valueOf
public static MyType valueOf(String name) {
return map.get(name);
}
public MyType(String name, String httpMethodType) {
this.name = name;
this.httpMethodType = httpMethodType;
map.put(name, this);
}
// accessors
public String name() { return name; }
public String httpMethodType() { return httpMethodType; }
// predefined constants
public static final MyType FIRST = new MyType("FIRST", "first");
public static final MyType SECOND = new MyType("SECOND", "second");
...
}
Think about Enum like a final class with static final instances of itself. Of course you cannot extend final class, but you can use non-final class with static final instances in your library. You can see example of this kind of definition in JDK. Class java.util.logging.Level can be extended with class containing additional set of logging levels.
If you accept this way of implementation, your library code example can be like:
public class EnumClass {
public static final EnumClass FIRST = new EnumClass("first");
public static final EnumClass SECOND = new EnumClass("second");
public static final EnumClass THIRD = new EnumClass("third");
private String httpMethodType;
protected EnumClass(String name){
this.httpMethodType = name;
}
}
Client application can extend list of static members with inheritance:
public final class ClientEnum extends EnumClass{
public static final ClientEnum CUSTOM_FIRST = new ClientEnum("custom_first");
public static final ClientEnum CUSTOM_SECOND = new ClientEnum("custom_second");
private ClientEnum(String name){
super(name);
}
}
I think that this solution is close to what you have asked, because all static instances are visible from client class, and all of them will satisfy your generic wildcard.
We Fixed enum inheritance issue this way, hope it helps
Our App has few classes and each has few child views(nested views), in order to be able to navigate between childViews and save the currentChildview we saved them as enum inside each Class.
but we had to copy paste, some common functionality like next, previous and etc inside each enum.
To avoid that we needed a BaseEnum, we used interface as our base enum:
public interface IBaseEnum {
IBaseEnum[] getList();
int getIndex();
class Utils{
public IBaseEnum next(IBaseEnum enumItem, boolean isCycling){
int index = enumItem.getIndex();
IBaseEnum[] list = enumItem.getList();
if (index + 1 < list.length) {
return list[index + 1];
} else if(isCycling)
return list[0];
else
return null;
}
public IBaseEnum previous(IBaseEnum enumItem, boolean isCycling) {
int index = enumItem.getIndex();
IBaseEnum[] list = enumItem.getList();
IBaseEnum previous;
if (index - 1 >= 0) {
previous = list[index - 1];
}
else {
if (isCycling)
previous = list[list.length - 1];
else
previous = null;
}
return previous;
}
}
}
and this is how we used it
enum ColorEnum implements IBaseEnum {
RED,
YELLOW,
BLUE;
#Override
public IBaseEnum[] getList() {
return values();
}
#Override
public int getIndex() {
return ordinal();
}
public ColorEnum getNext(){
return (ColorEnum) new Utils().next(this,false);
}
public ColorEnum getPrevious(){
return (ColorEnum) new Utils().previous(this,false);
}
}
you could add getNext /getPrevious to the interface too
#wero's answer is very good but has some problems:
the new MyType("FIRST", "first"); will be called before map = new HashMap<>();. in other words, the map will be null when map.add() is called. unfortunately, the occurring error will be NoClassDefFound and it doesn't help to find the problem. check this:
public class Subject {
// predefined constants
public static final Subject FIRST;
public static final Subject SECOND;
private static final HashMap<String, Subject> map;
static {
map = new HashMap<>();
FIRST = new Subject("FIRST");
SECOND = new Subject("SECOND");
}
private final String name;
public Subject(String name) {
this.name = name;
map.put(name, this);
}
// replacement for Enum.valueOf
public static Subject valueOf(String name) {
return map.get(name);
}
// accessors
public String name() {
return name;
}
Related
public class TableContent {
public static String EXCEL_SHEET_NAME = Nit.THEAD.getName();
public static String FILENAME= Nit.FILENAME.getName();
public enum Nit {
FILENAME("Nit-workorder-list"),
THEAD("NIT WORKORDER"),
TENDERSPECNO("TENDER SPECFICATION NO."),
FEE("TENDER FEE"),
SDAMOUNT("SD AMOUNT"),
TYPE("NIT TYPE"),
PRE_BID("PRE BIDDING DATE"),
OPEN_DATE("OPENING DATE"),
STATUS("CONTRACTOR STATUS");
private final String name;
public String getName() {
return name;
}
private Nit(String name) {
this.name = name;
}
public static Nit getNitHeadByName(String name)
{
Nit[] nit=Nit.values();
if(nit==null)
{
return null;
}
for(Nit nitHead:nit)
{
if(nitHead.getName().equals(name))
return nitHead;
}
return null;
}
public enum NitWorkOrder {
}
public enum NitList {
}
My objective is:
I want to export excel sheet from my application, every time I need to hardcode the table headings, which was not good programming practice.
So I use enum to overcome the hardcode problem. Now there are different table heading according to the list, then I enclosed all the required ENUMS in single class.
I used to write getXXXByName() and getXXXByValue() to access the enum, by name or by value.
But he problem is I need to write getXXXByName() and getXXXByValue() everytime inside each enum. I want to write these methods inside the class and outside the enums, and access those methods with the help of class name.
I just want to declare my constants inside enum.
Please kindly suggest me an idea or a way so I can make this method universal which will work for each and every enum. I want to write these methods in such a way so it can be accessed for all enums enclosed in my class. I thought about generics but I have little knowledge.
You can use generics to push functionality up to a parent class by telling the parent class that the type is an enum that implements an interface.
// Use an interface to inform the super class what the enums can do.
public interface Named {
public String getName();
}
// Super class of all Tables.
public static class Table<E extends Enum<E> & Named> {
private final Class<E> itsClass;
private final String sheetName;
private final String fileName;
public Table(Class<E> itsClass) {
this.itsClass = itsClass;
// Walk the enum to get filename and sheet name.
String sheetName = null;
String fileName = null;
for ( E e: itsClass.getEnumConstants() ){
if ( e.name().equals("FILENAME")) {
fileName = e.getName();
}
if ( e.name().equals("THEAD")) {
sheetName = e.getName();
}
}
this.sheetName = sheetName;
this.fileName = fileName;
}
// Use the interface and the enum details to do your stuff.
public E getByName (String name) {
for ( E e: itsClass.getEnumConstants() ){
if ( e.getName().equals(name)) {
return e;
}
}
return null;
}
}
// Extend Table and tell it about your enum using the super constructor.
public static class TableContent extends Table<TableContent.Nit> {
public TableContent() {
super(TableContent.Nit.class);
}
public enum Nit implements Named{
FILENAME("Nit-workorder-list"),
THEAD("NIT WORKORDER"),
TENDERSPECNO("TENDER SPECFICATION NO."),
FEE("TENDER FEE"),
SDAMOUNT("SD AMOUNT"),
TYPE("NIT TYPE"),
PRE_BID("PRE BIDDING DATE"),
OPEN_DATE("OPENING DATE"),
STATUS("CONTRACTOR STATUS");
private final String name;
Nit(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
For some background, I'm working on some framework stuff for a programming language I'm developing (JVM language, that is), and was testing some of the framework with Java classes, hence all the weird wrappers below.
So, my questions is, how do I get the type variables of a type parameter's bounds? Currently I have the following:
public static TemplateGenerics of(Class clazz) {
TemplateGenerics generics = new TemplateGenerics(); //TemplateGenerics is a wrapper class for generics that appear in the class header
Stream.of(clazz.getTypeParameters()).forEach(typeVariable -> {
java.lang.reflect.Type b = typeVariable.getBounds()[0];
try {
Class c = Primitives.resolveClass(b.getTypeName().split("<", 2)[0]); //Is there a better way to do this?
TemplateGenerics sub = TemplateGenerics.of(c); //Recursivley get the generics - it fails here
generics.getConditionals().add(new Conditional(new Type.Hierarchical(sub, c.getName()), Conditional.Condition.EXTENDS, typeVariable.getName())); //Conditional is another wrapper class that handles bounds of the generic,
//Type.Hierachical is yet another wrapper class that wraps types that appear in class headers
} catch (ClassNotFoundException e) {
throw new RuntimeException(e); //For testing purposes
}
});
return generics;
}
But this fails with a StackOverflowException when it encounters something like this:
public class A<T extends A<T>> ...
Since it just continues trying to get the type parameters of A over and over. I've been unable to find a method of getting the type variable's type variables... I've tried messing around with getGenericDeclaration, but it does not seem to return what I need. Any help is greatly appreciated.
#csharpfolk was right to suggest keeping a tally of what has already been parsed and making leverage of that. Below is a compile & runnable example which demos what this looks like in practice for your problem.
package so.answers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
public class TemplateGenerics {
private final List<Conditional> conditionals = new ArrayList<>();
public List<Conditional> getConditionals(){
return conditionals;
}
public String toString(){
return getConditionals().toString();
}
public static TemplateGenerics of(Class<?> clazz) {
return TemplateGenerics.of(clazz, new HashMap<>());
}
private static TemplateGenerics of(Class<?> clazz, Map<Class<?>, TemplateGenerics> existingGenericsForClasses) {
if(existingGenericsForClasses.containsKey(clazz)){
return existingGenericsForClasses.get(clazz);
}
final TemplateGenerics generics = new TemplateGenerics();
existingGenericsForClasses.put(clazz, generics);
Stream.of(clazz.getTypeParameters()).forEach(typeVariable -> {
java.lang.reflect.Type b = typeVariable.getBounds()[0];
try {
Class<?> c = Primitives.resolveClass(b.getTypeName().split("<", 2)[0]); //Is there a better way to do this?
TemplateGenerics sub = TemplateGenerics.of(c, existingGenericsForClasses); //Recursivley get the generics - it fails here
generics.getConditionals().add(new Conditional(new Type.Hierarchical(sub, c.getName()), Conditional.Condition.EXTENDS, typeVariable.getName())); //Conditional is another wrapper class that handles bounds of the generic,
//Type.Hierachical is yet another wrapper class that wraps types that appear in class headers
} catch (ClassNotFoundException e) {
throw new RuntimeException(e); //For testing purposes
}
});
return generics;
}
public static class Conditional{
public static enum Condition{
EXTENDS,
SUPER
}
private final Type.Hierarchical hierarchical;
private final Condition condition;
private final String typeName;
public Conditional(Type.Hierarchical hierarchical, Condition condition, String typeName){
this.hierarchical = hierarchical;
this.condition = condition;
this.typeName = typeName;
}
public String toString(){
return "Conditional$typeName="+typeName+" "
+"Conditional$condition="+condition+" "
+"Conditional$hierarchical={"+hierarchical+"} ";
}
}
public static class Primitives{
public static Class<?> resolveClass(String name) throws ClassNotFoundException{
String trimmedName = name.replaceFirst(TemplateGenerics.class.getCanonicalName()+".", "");
//not sure why this nonsense with the trimmed name
//is necessary, but you seem to already have a better
//version of this method anyway
if(trimmedName.contains(TemplateGenerics.class.getCanonicalName())){
name = trimmedName;
}
return Primitives.class.getClassLoader().loadClass(name);
}
}
public static class Type{
public static class Hierarchical{
private TemplateGenerics generics;
private String name;
public Hierarchical(TemplateGenerics generics, String name){
this.generics = generics;
this.name = name;
}
private boolean printing;
public String toString(){
try{
if(!printing){
printing = true;
return "Hierarchical$name="+name+ " Hierarchical$generics=("+generics+")";
} else {
return "Hierarchical$name="+name;
}
} finally {
printing = false;
}
}
}
}
public static class B{
}
public static class C<T extends B>{
}
public static class A<T extends A<T>>{
}
public static class X<T extends Y>{
}
public static class Y<T extends X>{
}
public static void main(String[] args){
System.out.println("For A:"+TemplateGenerics.of(A.class));
System.out.println("For C:"+TemplateGenerics.of(C.class));
System.out.println("For X:"+TemplateGenerics.of(X.class));
}
}
Output:
For A:[Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$A Hierarchical$generics=([Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$A} ])} ]
For C:[Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$B Hierarchical$generics=([])} ]
For X:[Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$Y Hierarchical$generics=([Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$X Hierarchical$generics=([Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$Y} ])} ])} ]
You could clean up the printing to look a little less redundant by printing the type rather than the generics directly. But this shows all the salient features of the solution.
Currently I have several enums defined over several classes. They all look similar to the one shown below:
public class ApaMessage {
private String apaMessage;
private final int FIXED_LENGTH_SIZE=39;
public enum ApaFields {
FIELD1(ApaUtils.ApaFieldTypes.POSITION_BASED, null, "field1", 2, 3, false, false),
private final ApaUtils.ApaFieldTypes type;
private final String ApaName;
private final String jsonName;
private final int start;
private final int finish;
private boolean required = false;
private boolean withDelimiter = false;
ApaFields(ApaUtils.ApaFieldTypes type, String ApaName, String jsonName, int start, int finish, boolean required, boolean withDelimiter) {
this.type = type;
this.ApaName = ApaName;
this.jsonName = jsonName;
this.start = start;
this.finish = finish;
this.required = required;
this.withDelimiter = withDelimiter;
}
}
There is also a method defined in ApaMessage:
private HashMap<String,Object> getApaJsonFieldsAndValues() {
HashMap<String, Object> jsonApaData = new HashMap<String, Object>();
for (ApaFields field : ApaFields.values()) {
jsonApaData.put(field.jsonName, getApaFieldValue(field));
}
return jsonApaData;
}
The problem is although there isn't a lot of code, I will soon have 10-20 of these enums. I would like to create an abstract base class where the HashMap method, and other similar methods can be part of. The base class should accept an ApaFields enum and other enums and do what the getApaJsonFieldsAndValues does. The problem is, how can the base class access the passed enum values and the internal fields such as jsonName to do the loop?
I have tried different approaches but the main problem is that the base class cannot seem to access the values. Is there any way around this? Alternatively, is there a better approach? Thanks
EDIT:
Basically I would like something like this in the base class. Note the below doesn't compile.
public abstract class ApaRequestMessage {
private Class<? extends Enum<?>> apaRequestMessageFields;
private String apaMessage;
public <T extends Enum<T>> void ApaRequest(Object apaRequestFields, String apaMessage) {
apaRequestMessageFields = (Class<? extends Enum<?>>) apaRequestFields;
this.apaMessage = apaMessage;
for (Field field: apaRequestMessageFields.values()) {
//this doesn't work because it cannot access the values of apaRequestMessageFields
}
}
}
And then call the base method as follows, although not sure if this is correct, where ApaFields is the inner enum defined above.
ApaRequest(ApaFields.class, somestringmessage);
I came across something similar when trying to define a db schema using enums as columns in the table. I eventually took this route.
Define a base class with sufficient generic signature to ensure the enum is properly built.
public class Table<Column extends Enum<? extends Column>> {
// Name of the table.
protected final String tableName;
// All of the columns in the table. This is actually an EnumSet so very efficient.
protected final Set<Column> columns;
/**
* The base interface for all Column enums.
*/
public interface Columns {
// What type does it have in the database?
public Type getType();
}
// Small list of database types.
public enum Type {
String, Number, Date;
}
public Table(String tableName,
Set<Column> columns) {
this.tableName = tableName;
this.columns = columns;
}
}
Now extend this for each table - here is a simple VersionTable:
public class VersionTable extends Table<VersionTable.Column> {
public enum Column implements Table.Columns {
Version(Table.Type.String),
ReleaseDate(Table.Type.Date);
final Table.Type type;
Column(Table.Type type) {
this.type = type;
}
#Override
public Type getType() {
return type;
}
}
public VersionTable() {
super("Versions", EnumSet.allOf(Column.class));
}
}
Now you have all of the core functionality in the base class and all the sub-classes need to do is implement the interface on the enum.
I realise this does not address the issue of duplicated bolierplate code in all of your enums but it does move alomst all of it elsewhere.
I would like to make a generic method to get a List from the parameter object.
The problem is because I have a declared object with a instance of the other class that extends the declared class.
I don't want to use the instanceof solution because the number of classes that extends LimitedValue can be big.
I thought to use reflection for a solution, but I don't know how to use that with an instance of object, in this part of the code:
Class cls = Class.forName(limitedValue.getClass().getName());
Object obj = cls.newInstance();
//This is wrong, I don't want a new instance.
Method[] methods = cls.getDeclaredMethods();
for(int x= 0; x < methods.length; x++) {
Method method = methods[x];
if ("java.util.List".equals(method.getReturnType().getName())) {
//How to get the value of this method from limitedValue instance ?
}
}
This is my full code:
public class CalculatorLimitedValue {
public static void main(String[] args) throws Exception {
StoreItem storeItem = new StoreItem(1L, "Name of StoreItem", 50L);
List listOfStoreItems = new ArrayList();
listOfStoreItems.add(storeItem);
LimitedValue limitedValue0 = new Store(listOfStoreItems);
List firstList = calculator(limitedValue0);
//do something with the list
SupermarketItem supermarketItem = new SupermarketItem(1L, "Name of SupermarketItem", 21L);
List listOfSupermarketItems = new ArrayList();
listOfSupermarketItems.add(supermarketItem);
LimitedValue limitedValue1 = new Supermarket(listOfSupermarketItems);
List secondList = calculator(limitedValue1);
//do something with the list
}
/** This is the method that I'd like to make generic to return a List */
private static List calculator(LimitedValue limitedValue) throws Exception{
Class cls = Class.forName(limitedValue.getClass().getName());
Object obj = cls.newInstance();
//This is wrong, I don't want a new instance.
Method[] methods = cls.getDeclaredMethods();
for(int x= 0; x < methods.length; x++) {
Method method = methods[x];
if ("java.util.List".equals(method.getReturnType().getName())) {
//How to get the value of this method from limitedValue instance ?
}
}
/* I don't want to use this one way, because my classes that extends LimitedValue
can be big. I would like to made a generic way to get de list of classes. */
if (limitedValue instanceof Store) {
System.out.println("This is a store");
return ((Store) limitedValue).getStoreItems();
} else if (limitedValue instanceof Supermarket) {
System.out.println("This is a supermarket");
return ((Supermarket) limitedValue).getSupermarketItems();
}
return null;
}
}
If it help, these are my other classes:
LimitedValue.class
public class LimitedValue { }
StoreItem.class
public class StoreItem {
private Long id;
private String nameOfStoreItem;
private Long valueOfStoreItem;
public StoreItem(Long id, String nameOfStoreItem, Long valueOfStoreItem){
this.id = id;
this.nameOfStoreItem = nameOfStoreItem;
this.valueOfStoreItem = valueOfStoreItem;
}
//getters and setters...
}
SupermarketItem.class
public class SupermarketItem {
private Long id;
private String nameOfSupermarketItem;
private Long valueOfSupermarketItem;
public SupermarketItem() {
}
public SupermarketItem(Long id, String nameOfSupermarketItem, Long valueOfSupermarketItem) {
this.id = id;
this.nameOfSupermarketItem = nameOfSupermarketItem;
this.valueOfSupermarketItem = valueOfSupermarketItem;
}
//getters and setters...
}
Store.class
public class Store extends LimitedValue {
private List<StoreItem> storeItems;
public Store(List<StoreItem> storeItems) {
this.storeItems = storeItems;
}
//getters and setters
}
Supermarket.class
public class Supermarket extends LimitedValue {
private List<SupermarketItem> supermarketItems;
public Supermarket(List<SupermarketItem> supermarketItems) {
this.supermarketItems = supermarketItems;
}
//getters and setters
}
You could try to use reflection here to try to achieve what you want, but it would be better to reconsider your overall design and try to use a better object oriented design that solves the problem at hand.
In particular, lets say we consider adding a method called getItems to the LimitedValue class that returns a List of items, which may be SupermarketItems or may be StoreItems. If it is structured correctly, you won't need to know the actual type because the code will be abstracted over it polymorphically.
public abstract class LimitedValue {
List<? extends Item> getItems();
}
We've now defined a new method on LimitedValue, but we also have to consider that we've introduced this new Item thing. I note that the SupermarketItem and StoreItem all share similiar attributes, name, id and value, so it seems that it might be possible to use a single class to represent them all.
public abstract class Item {
final Long id;
final String name;
final Long value;
public Item(final Long id, final Long name, final Long value) {
this.id = id;
this.name = name;
this.value = value;
}
String getName() {
return name;
}
// other getters and setters
}
public class SupermarketItem extends Item {
public SupermarketItem(final Long id, final Long name, final Long value) {
super(id, name, value);
}
}
public class StoreItem extends Item {
public StoreItem(final Long id, final Long name, final Long value) {
super(id, name, value);
}
}
Now we've completely abstracted away the need for any reflection when accessing these objects - you can simply call item.getValue() as you will know that every item in the list is of type Item.
Of course, you'll also need to refactor the Store and SuperMarket classes, for example:
public class Supermarket extends LimitedValue {
private List<SupermarketItem> supermarketItems;
public Supermarket(List<SupermarketItem> supermarketItems) {
this.supermarketItems = supermarketItems;
}
public List<? extends Item> getItems() {
return supermarketItems;
}
}
and because you are only returning a List<Item> you always know what is in it, and you can change your main code to work with this.
This is a much cleaner long term solution.
To get the List value, use Method#invoke:
List list = method.invoke(limitedValue);
You don't need Object obj = cls.newInstance(); - you're not using it at all in the method.
In any case, you're making it very difficult for yourself. You could also define an interface
public interface HasList<E> {
List<E> getList();
}
and have all classes implement this.
For instance i have some entity - Product
public class Product {
...
private String name;
private int count;
private Product associatedProduct;
...
// GETTERS & SETTERS
}
And also i have product finder which allows to find the product by filters:
public interface Finder<T> {
Set<T> find(Filter... filters);
}
And now i can execute the following code:
Finder<Product> finder = ...;
// find all products with name 'cucumber'
Set<Product> finder.find(Filter.equals("name", "cucumber"));
We don't like this code because i should have the 'soft' link to field name "name" and i can't have compile time exception in case misprint or any other mistake.
For this reason i have created the code generator which generate static links to properties.
The generated class looks like:
public final class $Product {
private final String context;
// some factory is used to instance creation
$PostEntity() {this.context = "";}
$PostEntity(String context) {this.context = context;}
public String name() { return context + "name";}
public String count() { return context + "count";}
public String associatedProduct() { return context + "associatedProduct";}
public $Product associatedProductDot() { return new $Product( this.context + "associatedProduct.");}
}
For now i can make the following:
Set<Product> finder.find(Filter.equals(Links.PRODUCT.name() , "cucumber"));
//or
Set<Product> finder.find(Filter.equals(Links.PRODUCT.associatedProductDot().name() , "cucumber"));
It works like a charm and i happy.
I know alternative approach with using proxy objects, but it imposes additional overhead in runtime and adds some magical moment in code, so this variant does not suit me.
And finally my question:
There is a more elegant approach to implement this functionality with using java 8?
Java 8 has everything you need:
public static <C,P> Predicate<C> byProperty(Function<C,P> f, P value) {
return component->Objects.equals(f.apply(component), value);
}
public static <C> Set<C> find(Collection<? extends C> c, Predicate<? super C> p) {
return c.stream().filter(p).collect(Collectors.<C>toSet());
}
The standard interface for filtering is called Predicate and the first method above allows you to create arbitrary Predicates for matching a property of a component type C. The second method shows how you can get a Set of matching components out of a Collection using the Stream API. Then you can use it like this:
List<Product> list;
…
Set<Product> set=find(list, byProperty(Product::getName, "foo"));
or
Set<Product> set=find(list, byProperty(Product::getCount, 42));
Note that this is type safe and contains compile-time checked references (your “hard links”) to your properties. The only difference to what you have asked for is that they refer to the getter method rather than to the field names, as a) field references are not supported and b) your fields are private anyway.
Note that you can augment these methods by another factory allowing to provide a value-predicate rather than a constant:
public static <C,P> Predicate<C> matchProp(
Function<C,P> f, Predicate<? super P> value) {
return component->value.test(f.apply(component));
}
This allows use cases like:
Set<Product> set=find(list, matchProp(Product::getCount, count -> count>100));
See Lambda Expressions
or
Set<Product> set=find(list, matchProp(Product::getName, String::isEmpty));
The fastest thing is to provide your own implementation of the Filter interface. Since I don't know your Filter interface, I have to make an assumption about how it looks like. Here is my assumption:
public interface Filter<T> {
boolean matches(T t);
}
By the way, I think the interface Finder should instead look like this:
public interface Finder<T> {
Set<T> find(Filter<? super T>... filters);
}
So, you could have a class like this:
public final class ProductFilters {
private ProductFilters() { /* Utility class */ }
public static Filter<Product> byName(final String name) {
return new Filter() {
public boolean matches(Product t) {
return name.equals(t.getName());
}
}
}
}
You could even put this inside class Product, which can make it a little bit nicer:
public class Product {
private String name;
public static final class Filters {
private Filters() { /* Utility Class */ }
public static Filter<Product> byName(final String name) {
return new Filter() {
public boolean matches(final Product t) {
return name.equals(t.name);
}
};
}
}
}
And yes, Java 8 makes this stuff nicer, the explicit anonymous class can syntactically be replaced by a lambda, like this:
public class Product {
private String name;
public static final class Filters {
private Filters() { /* Utility Class */ }
public static Filter<Product> byName(final String name) {
return t -> name.equals(t.name);
}
}
}
Your code that uses the filters could now look like this:
Set<Product> cucumbers = finder.find(Product.Filters.byName("cucumber"));
The Filter<T> interface is present in Java 8 in package java.util.function. It's name there is Predicate<T>, and the essential part looks like this:
public interface Predicate<T> {
boolean test(T t);
}
If the products that are to be filtered can be made available as Stream either directly, or via a Collection, you can use the new java.util.stream API for filtering. For the example I assume that the products to be filtered are in a Set, too. The code that filters products could look like this:
Set<Product> potentialCucumbers = ...;
// Inline lambda:
Set<Product> cucumbers = potentialCucumbers.stream().filter(p -> "cucumber".equals(p.getName())).collect(Collectors.toSet());
// Stored lambda as above:
Set<Product> cucumbers = potentialCucumbers.stream().filter(Product.Filters.byName("cucumber")).collect(Collectors.toSet());
I really like static imports for that stuff as they can significantly reduce line length. With static imports it looks like this:
Set<Product> potentialCucumbers = ...;
// Inline lambda:
Set<Product> cucumbers = potentialCucumbers.stream().filter(p -> "cucumber".equals(p.getName())).collect(toSet());
// Stored lambda as above:
Set<Product> cucumbers = potentialCucumbers.stream().filter(byName("cucumber")).collect(toSet());
My suggestion would be to use predicates rather than your Filter classes. They make for cleaner code. I would also suggest making commonly used properties like "name" or "owner" into interfaces that provide predicates for searchability. For instance, for the "name" and "owner" properties you might have two interfaces called "Named" and "Owned":
public interface Named {
public String getName();
public void setName(String name);
static <T extends Named> Predicate<T> nameEquals(Class<T> clazz, String s){
return ((p) -> {
if (s == null){
return p.getName() == null;
}
return s.equals(p.getName());
});
}
}
public interface Owned {
public String getOwner();
public void setOwner(String owner);
public static <T extends Owned> Predicate<T> ownerEquals(Class<T> clazz, String s){
return ((p) -> {
if (s == null){
return p.getOwner() == null;
}
return s.equals(p.getOwner());
});
}
}
Then your Product class implements these interfaces, along with a couple simple convenience methods for calling the interface static methods:
public class Product implements Named, Owned{
private String name;
private String owner;
public String getOwner() {
return owner;
}
public String getName() {
return name;
}
public void setOwner(String owner){
this.owner = owner;
}
public void setName(String name){
this.name = name;
}
public static Predicate<Product> nameEquals(String s){
return Named.nameEquals(Product.class, s);
}
public static Predicate<Product> ownerEquals(String s){
return Owned.ownerEquals(Product.class, s);
}
}
And voila, your Product is searchable. Then your find() method's signature changes to take a predicate:
public interface Finder<T> {
Set<T> find(Predicate p);
}
One of the wonderful things about predicates is how easy they are to combine and compound with one another. For example, let's say we want to find() any products named "cucumber" who aren't owned by "john", or any products owned by "john" with any other names. The call to find() is pretty clean and understandable:
finder.find(
Product.nameEquals("cucumber")
.and(Product.ownerEquals("john").negate())
.or(
Product.ownerEquals("john")
.and(Product.nameEquals("cucumber").negate())
)
);
I should be pretty clear what this block of code is doing. I used the indentation to try and make it clearer how they combine. We can combine the different predicates to our hearts' content.