Design query - Using Java Generics for code duplication - java

Problem
I am trying to design two methods that filter a list
public List<TypeA> filter(List<TypeA> typeAList) {
//duplicated code
//filter typeAList using some parameters in typeA objects
//return filtered list
}
public List<TypeB> filter(List<TypeB> typeBList) {
//duplicated code
//filter typeBList using some parameters in typeB objects
//return filtered list
}
The problem is both the methods have duplicate code except for the filtering part where I access different parameters inside TypeA and TypeB.
Things I tried so far
I tried making a generic method like this. But this does not support
types other than TypeA and TypeB. Encourages someone to call this
method with an unintended type.
public <T> List<T> filter(List<T> genericList) {
//duplicated code
if (T instanceOf TypeA)
//filtering code for TypeA
if (T instanceOf TypeB)
//filtering code for TypeB
//return filtered list
}
Overload with two methods calling a private generic filter method. I
felt this ensures unintended calls to the public method, while still
using generics to avoid code duplication.
public List<TypeA> filter(List<TypeA> typeAList) {
//call innerFilter(typeAList)
}
public List<TypeB> filter(List<TypeB> typeBList) {
//call innerFilter(typeBList)
}
private <T> List<T> innerFilter(List<T> genericList) {
//duplicated code
if (T instanceOf TypeA)
//filtering code for TypeA
if (T instanceOf TypeB)
//filtering code for TypeB
//return filtered list
}
Tried to make the two classes implement a common interface and use
that interface as the input parameter to my method. But one of the
class is third-party and not under my control.
Help needed
I'm really new to design. Want to understand if my reasoning is right behind the approaches. Also looking for suggestions on alternate best approaches to solve this problem. Thanks in advance.

This is exactly the type of problem that the Predicate<T> functional interface was meant to solve.
import java.util.List;
import java.util.function.Predicate;
public class SOQ_20220501
{
public static void main(String[] args)
{
record TypeA(int a) {}
record TypeB(boolean b) {}
final List<TypeA> as = List.of(new TypeA(0), new TypeA(1), new TypeA(2), new TypeA(3), new TypeA(4));
final List<TypeB> bs = List.of(new TypeB(true), new TypeB(false));
var whateverA = filter(as, typeA -> typeA.a() % 2 == 1);
System.out.println(whateverA);
var whateverB = filter(bs, typeB -> typeB.b());
System.out.println(whateverB);
}
public static <T> List<T> filter(List<T> typeAList, Predicate<T> predicate)
{
return
typeAList.stream()
.filter(predicate)
.toList()
;
}
}

The appropriate structure is not reflective, and does not use instanceof.
public List<TypeA> filter(List<TypeA> typeAList) {
innerFilter(typeAList, typeA -> isGoodA(typeA))
}
private boolean isGoodA(TypeA a) { ... }
public List<TypeB> filter(List<TypeB> typeBList) {
innerFilter(typeBList, typeB -> isGoodB(typeB))
}
private boolean isGoodB(TypeB a) { ... }
private <T> List<T> innerFilter(List<T> genericList, Predicate<T> pred) {
//duplicated code
//filter genericList using pred
//return filtered list
}

Assume you have this 2 TypeX interface without inheritance link and with same methods signature.
interface TypeA {
String methodFromA();
}
interface TypeB {
String methodFromB();
}
You could declare an Enum who knows which method has to be called for each TypeX interface.
enum FilterType {
TYPE_A(TypeA.class){
#Override
public <T> void callMethod(T typeX) {
TypeA typeA = (TypeA) typeX;
typeA.methodFromA();
}
},
TYPE_B(TypeB.class){
#Override
public <T> void callMethod(T typeX) {
TypeB typeB = (TypeB) typeX;
typeB.methodFromB();
}
};
Class typeClass;
FilterType(Class typeClass) {
this.typeClass = typeClass;
}
public static FilterType from(Class<?> typeClass) {
return Arrays.stream(values())
.filter(filterType -> filterType.typeClass.equals(typeClass))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("FilterType for class '" + typeClass + "' not exist")),
}
public abstract <T> void callMethod(T typeX);
}
Finally, in your filter method, you just have to recover the enum instance with the TypeX class and call the appropriated method on it.
class FilterService<T> {
// The class type of TypeX interface
private final Class<T> typeClass;
public FilterService() {
// Recover the class of the generic T
this.typeClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
public List<T> filter(List<T> genericList) {
FilterType filterType = FilterType.from(typeClass); // Will throw IllegalArgumentException if T class isn't handle
genericList.forEach(typeX -> filterType.callMethod(typeX));
//return filtered list
}
}

Related

Java - Generics - Casting generic object in generic specified object doesn't work

I'm trying to resolve this apparently simple generic casting problem :
First, declaring this simple generic object :
public interface GenericObject<T> {}
Second, declaring this working interface :
public interface Generic { // I don't want to do Generic<T>
<T> void setGenericObject(GenericObject<T> obj);
}
Then, let's implements this interface :
public class GenericImpl implements Generic {
private GenericObject<String> genericObject; // This is needed
#Override
public <String> void setGenericObject(GenericObject<String> obj) {
genericObject = obj; // eclipse give me this error :
// Type mismatch: cannot convert from
// interfaces.GenericObject<String> to
// interfaces.GenericObject<java.lang.String>
}
}
How can I solve this error ?
Edit :
Actualy, the only way I have to solve this issue is to do this :
public class GenericImpl implements Generic {
private GenericObject<String> genericObject;
#SuppressWarnings("unchecked") // I don't realy like this
#Override
public <T> void setGenericObject(GenericObject<T> obj) {
genericObject = (GenericObject<String>) obj;
}
}
The real problem is that
public <String> void setGenericObject(GenericObject<String> obj)
where the String has nothing to do with the your intended java.lang.String. Here the String is just a type parameter whose name is String by accident.
Please refer to Is it possible to have an interface method defined with a generic return type and a concrete implementation define the return type?.
Case 1:
If T is not used in Generic, then just use a wildcard.
class Generic {
List<?> list;
void set(List<?> list) {
this.list = list;
}
int size() {
return list.size(); // doesn't care about T
}
}
Case 2:
If T is only used as local variables, then declare <T> on the method
class Generic {
<T> void swapFirstAndSecond(List<T> list) {
T first = list.get(0), second = list.get(1);
list.set(1, first);
list.set(0, second);
}
}
Case 3:
If several fields and methods use the same type T, but the exact type of T is not important, then delacre <T> on the class
class Generic<T> {
List<T> list;
void set(List<T> list) {
this.list = list;
}
T getFirst() {
return list.get(0);
}
}
Case 4:
If T must be a specific type, like String, then don't declare type parameter <T>
class Generic {
List<String> list;
void set(List<String> list) {
this.list = list;
}
boolean isFirstContainsSecond() {
String first = list.get(0), second = list.get(1);
// call String.contains here, so T must be String
return first.contains(second);
}
}

Effective version of writing the following method

Is there an elegant and best approach to write the following method?
private void throwException(Object obj) {
if (obj instanceof ClassA) {
ClassA resp = (ClassA) obj;
throw new CustomException(resp.getMessage(), resp.getCode());
} else if (obj instanceof ClassB) {
ClassB resp = (ClassB) obj;
throw new CustomException(resp.getMessage(), resp.getCode());
}
}
Note that ClassA and ClassB has the same exact properties. My point is that, I don't want to repeat the throw statement as much as possible.
Define a map like
Map<Class<?>, Function<Object, ? extends RuntimeException>> handlers = new LinkedHashMap<>();
The handlers will contain a Function that you can use to map the Object passed to throwException to get an exception. The key of the map is a class and value is the function that will map an object of the type (class) of the key to an exception.
Populate the above map as
handlers.put(ClassA.class, (obj) -> new CustomException(((ClassA) obj).getMessage(), ((ClassA) obj).getCode));
handlers.put(ClassB.class, (obj) -> new CustomException(((ClassB) obj).getMessage(), ((ClassB) obj).getCode));
With this, throwException would look like,
private void throwException(Object obj) {
Function<Object, ? extends RuntimeException> handler = handlers.entrySet().stream()
.filter(entry -> entry.getKey().isAssignableFrom(obj.getClass()))
.map(Map.Entry::getValue)
.findFirst()
.orElseThrow(() -> new RuntimeException("No handler found")); //Or use a default using orElseGet
throw handler.apply(obj);
}
I agree that it moves the casting elsewhere to make the method look clean.
The key part is the line
.filter(entry -> entry.getKey().isAssignableFrom(obj.getClass()))
We are checking if the object (passed to throwException) is of type returned by entry.getKey (the class of the map key). So, if you have inheritance hierarchy among the classes (ClassA, ClassB..), then you must populate the map in such an order so that the most generic ones (say like Object.class) comes after more specific ones.
A consequence of putting an entry into handlers with Object.class as the first entry would mean that the function (value) of Object.class will always be picked up for any object passed to it.
You can use Function<>s to wrap the getters into a custom interface beforehand.
interface Wrapper {
String getMessage();
int getCode();
<T> Function<T, Wrapper<T>> of(Function<T,String> getMsg, Function<T, Integer> getCde) {
return object -> new Wrapper() {
public String getMessage() { return getMsg.apply(object); }
public int getCode() { return getCde.apply(object); }
}
}
}
class Thrower {
Map<Class<?>, Supplier<Wrapper>> wrappers = new HashMap();
static {
wrappers.put(A.class, Wrapper.of(A.class, A::getMessage, A::getCode));
wrappers.put(B.class, Wrapper.of(B.class, B::getMessage, B::getCode));
}
void throwException(Object o) {
Wrapper wrapper = wrappers.get(o.getClass()).apply(o);
throw new CustomException(wrapper.getMessage(), wrapper.getCode());
}
}
You can kill two birds with one stone as this is a classical problem in the clean code design where you can choose to implement Visitor Design Pattern as a workaround to multiple If/else instanceof type of conditioning with a little bit of expansion to cover the problem of excessive throwing of new CustomExeption.
Here is what I can suggest you :
First it's better to change you design for ClassA and ClassB as:
abstract class ClassParent {
// your fields
public ClassParent (//your fields) {
// initializing your fields
}
public abstract void accept(ClassVisitor cv);
}
class ClassA extends ClassParent {
public ClassA(//your fileds) {
super(//your fileds);
}
/// other Implementation
public void accept(ClassVisitor cv) {
cv.visit(this);
}
}
class ClassB extends ClassParent {
public ClassB(//your fileds) {
super(//your fileds);
}
/// other Implementation
public void accept(ClassVisitor cv) {
cv.visit(this);
}
}
Now define your vistors as :
interface ClassVisitor {
abstract void visit(ClassA classA);
abstract void visit(ClassB classB);
}
class Visitor implements ClassVisitor {
public void visit(ClassA classA) {
classA.DoSomthing();
}
public void visit(ClassB classB) {
classB.DoSomthing();
}
}
Now it your ThrowException you can define :
private ClassVisitor visitor = new Visitor();
public void ThrowException(ClassParent classParent){
classParent.accept(visitor);
throw new CustomException(classParent.getMessage(), classParent.getCode);
}
This way you achieve both a cleaner code, more maintainable with
higher readablity by avoiding duplicate codes at the same time.

Using enum as a mapper

I have a lot of different objects which are being mapped so I've written a bunch of static mapping methods and one giant switch-case method which takes the type (a built-in field) and then uses the specialized mapping function.
Example:
public static SomeOtherObject mapFrom(SomeObject someObject) {
//... mapping logic
return someOtherObjectInstance;
}
//... a bunch of those
// root/main mapper
public static SomeOtherObjectBase mapFrom(SomeObjectBase someObjectBase) {
switch(someObjectBase.getType()) {
case SOME_TYPE: return mapFrom((SomeObject)someObjectBase);
//...
}
}
I then thought that I could probably convert this to an enum where each enumeration would be a mapper and would be bound to the type, thus avoiding a switch-case... something like this:
public enum SomeObjectMappers {
SOME_TYPE_MAPPER(SOME_TYPE) {
#Override
SomeOtherObject mapFrom(SomeObject someObject) {
//... mapping logic
return someOtherObjectInstance;
}
},
//... a bunch of those
;
private final Type type;
//constructor, getters...
abstract <T extends SomeOtherObjectBase, U extends SomeObjectBase> T mapFrom(U obj);
public static SomeOtherObjectBase mapFrom(SomeObjectBase obj) {
return Arrays.stream(values())
.filter(v -> v.getType() == type)
.map(m -> m.mapFrom(obj))
.findFirst()
.orElse(null);
}
}
However this does not really compile/work as for some reason mapper implementation in SOME_TYPE_MAPPERdoes not accept concrete subclasses SomeOtherObject and SomeObject as valid signatures for the abstract method.
Can this not be done?

Make a copy of a List with abstract type to a List with specific type

first of all my code below just delivers an abstract view of my classes so that you can easily understand what my question is about :) -so also no equals, hashcode.
I have an ABC extended from Observable which has a List of AbstractObservers
Then I have some classes B and C inherit from AbstractObservers. AbstractObservers implements Observer.
The focus is now at the Recover class.
With the method recover() I try to return a List with the concrete type (B or C) instead of AbstractObservers.
I am not satisfies with my solution, I think it could be better, easier?
Have you any idea how I could solve that problem better? The Abstract classes must stay due to Hibernate.
Thank you in advance
public abstract class ABCObservable extends Observable {
List<AbstractObserver> abstractObserverList = new LinkedList<>();
public List<AbstractObserver> getAbstractObserverList() {
return abstractObserverList;
}
#Override
public synchronized void addObserver(Observer o) {
super.addObserver(o);
abstractObserverList.add((AbstractObserver) o);
}
}
AbstractObserver
public abstract class AbstractObserver implements Observer {
#Override
public void update(Observable o, Object arg) {
}
}
B
public class B extends AbstractObserver {
}
C
public class C extends AbstractObserver {
}
Recover
public class Recover {
public List<? extends AbstractObserver> recover(ABCObservable abcObservable) {
List<AbstractObserver> returnList = new LinkedList<>(); //does that delivers a List with AbstractObserver or B or C?
if (abcObservable.getAbstractObserverList().get(0) instanceof B) {
returnList = new LinkedList<>();
returnList.addAll(abcObservable.getAbstractObserverList());
} else if (abcObservable.getAbstractObserverList().get(0) instanceof C) {
returnList = new LinkedList<>();
returnList.addAll(abcObservable.getAbstractObserverList());
}
return returnList; // returns a List with B or C elements
}
}
In Java, you can't convert a list to a new type, what you can do, however, is create a new one and add the values to it. If you use a generic method, you should be able to accomplish this. I haven't test this code, but theoretically it should work:
public <T> List<T> recover(T abcObservable) {
List<AbstractObserver> list = abcObservable.getAbstractObserverList();
List<T> returnList = new LinkedList<>();
returnList = new LinkedList<>();
for(AbstractObserver a : list) {
if(a instanceof T) {
returnList.add(a);
}
}
return returnList;
}
Instead of using a defined class, this snippet uses T, you can find out more about how Java Generic Methods work on this Java Tutorial by Oracle.
EDIT: I think I'm confused about what you are asking for, this doesn't answer your question, if I understand it correctly.

Java Multiple boundaries

what i'm trying to do is as follows:
assume that getClassFromString() and getAllObjectsFromRepositoryByClass() already exist.
why can't i use Class<T extends Named & HasId>.
i tried generifying the class itself, but can't use stuff like T.class etc.
public interface Named { String getDisplayName(); }
public interface HasId { String getId(); }
public class Foo {
public List<PairItem> getPairItems(String typeId) {
Class<T extends Named & HasId> clazz = getClassFromString(typeId);
List<T> allObjects = getAllObjectsFromRepositoryByClass(clazz);
List<PairItem> items = new ArrayList<>();
for (clazz obj : allObjects) {
items.add(obj.getDisplayName(),ibj.getId());
}
return items;
}
You can change you Foo-class in this way:
public class Foo {
public <T extends Named & HasId> List<PairItem> getPairItems(String typeId) {
Class<?> classFromString = getClassFromString(typeId);
// Java 8 seems to be unable to chain asSubclass-calls. These are merely to verify our unchecked cast
classFromString.asSubclass(Named.class);
classFromString.asSubclass(HasId.class);
//noinspection unchecked
return getPairItems((Class<T>) classFromString);
}
public <T extends Named & HasId> List<PairItem> getPairItems(final Class<T> clazz) {
List<T> allObjects = getAllObjectsFromRepositoryByClass(clazz);
List<PairItem> items = new ArrayList<>();
for (T obj : allObjects) {
items.add(new PairItem(obj.getDisplayName(), obj.getId()));
}
return items;
}
}
This fixes your problems with multiple boundaries as they are only allowed for type-parameters per documentation.; also I guess that
If one of the bounds is a class, it must be specified first.
leads to the problem that the asSubclass()-calls can not be chained, otherwise we could remove our unchecked cast.
The second method can profit from the streaming-API like this:
public <T extends Named & HasId> List<PairItem> getPairItems(final Class<T> clazz) {
List<T> allObjects = getAllObjectsFromRepositoryByClass(clazz);
return allObjects.stream()
.map(obj -> new PairItem(obj.getDisplayName(), obj.getId()))
.collect(Collectors.toList());
}
Overall I assumed that you wanted to instantiate PairItem and split the method so there is a unchecked and a fully-checked part.

Categories