Convert a method to a generic one to avoid duplicate code - java

I need some help with the following: I have multiple calls to a method that look like this:
private void saveA(myObjA myObj, List<A> myList) {
if (myList != null && !myList.isEmpty()) {
myObj.saveAll(myList);
}
}
private void saveB(myObjB myObj, List<B> myList) {
if (myList != null && !myList.isEmpty()) {
myObj.saveAll(myList);
}
}
...
Example of interface:
public interface myObjA
extends JpaRepository<A, Long> {
}
public interface myObjB
extends JpaRepository<B, Long> {
}
...
The thing is I'm creating a new one for all the other calls (myObjB, myListB, myObjC, myListC). myObj is actually an interface and the second parameter is always a list of some object. Is there any way to convert this method to a single one and specify the object type in the call?

This can be done using generic method:
public <T, N> void save(JpaRepository<T, N> repo, List<T> list) {
if (null != list && !list.isEmpty()) {
repo.saveAll(list);
}
}
// usage
#Autowired
private MyRepo repository; // MyRepo implements JpaRepository<MyObj, Long>
public void foo(List<MyObj> list) {
save(repository, list);
}

Related

Design query - Using Java Generics for code duplication

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
}
}

How to pass a getter as lambda function?

I want to pass the getter of a bean as a function. When the function is called the getter should be invoked. Example:
public class MyConverter {
public MyConverter(Function f) {
this.f = f;
}
public void process(DTO dto) {
// I just want to call the function with the dto, and the DTO::getList should be called
List<?> list = f.call(dto);
}
}
public class DTO {
private List<String> list;
public List<String> getList() { return list; }
}
Is that possible with java 8?
If the constructor of MyConverter must take a function, and process must take an object, this is probably the best way:
class MyConverter<T> {
// V takes a thing (in our case a DTO)
// V returns a list of Strings
private Function<T, List<String>> f;
public MyConverter(Function<T, List<String>> f) {
this.f = f;
}
public void process(T processable) {
List<String> list = f.apply(processable);
}
}
MyConverter<DTO> converter = new MyConverter<>(DTO::getList);
DTO dto = new DTO();
converter.process(dto);

Method not applicable for arguments with generics

I am no expert with generics and as I am trying to do a reengineering of some classes to avoid code repetition, I am forcing myself to use generics in order to do it the best way possible.
I am getting the next error in the lines I marked:
The method delete(Long) in the type CrudRepository is not applicable for the arguments (capture#5-of ? extends KeyProfileEntity)
Here my class:
public abstract class KeyProfileService {
protected CrudRepository<? extends KeyProfileEntity, Long> myRepository;
public List<KeyProfileEntity> getList() {
List<KeyProfileEntity> result = new ArrayList<>();
this.myRepository.findAll().forEach(result::add);
return result;
}
public KeyProfileEntity create(KeyProfileEntity entity) {
return this.myRepository.save(entity); //error
}
public boolean delete(long id) {
if (this.myRepository.exists(id)) {
this.myRepository.delete(this.myRepository.findOne(id));//error
return true;
}
return false;
}
public void update(KeyProfileEntity entity) {
this.myRepository.save(entity); //error
}
public KeyProfileEntity getEmployee(long id) throws NotFoundEntryException {
if (this.myRepository.exists(id))
return this.myRepository.findOne(id);
throw new NotFoundEntryException();
}
}
I think this is all the info you guys need, otherwise comment and I will attach more.
Thanks in advance!
You can fix it by removing the <? extends ...> bound wildcard from myRepository:
protected CrudRepository<KeyProfileEntity, Long> myRepository;
As far as I can see, your class will be still usable, even with subclasses of KeyProfileEntity:
KeyProfileService service = new KeyProfileServiceImpl();
service.update(new ChildKeyProfileEntity());
There will be only one limitation: getList() will always return a List<KeyProfileEntity>, not a List<ChildKeyProfileEntity>.
Alternatively, you can make the KeyProfileService generic and make sure you use a bound, known subtype:
public abstract class KeyProfileService<K extends KeyProfileEntity> {
protected CrudRepository<K, Long> myRepository;
public List<K> getList() { // using K
List<K> result = new ArrayList<>(); // here too
this.myRepository.findAll().forEach(result::add);
return result;
}
public K create(K entity) { // using K
return this.myRepository.save(entity);
}
...
}

Call method based on the type of the generic list element

I have a List defined with generics. All elements in this list define the interface TransferableData. Depending on the object that eventually populates the list, i want to call a method to do something with the data.
So I want to archive something like this:
private <T extends TransferableData> String doSomething(List<T> data, Class<T> dataType){
if(returnType.equals(A.class)){
for(T singleElement : data){
((A)singleElement).methodInsideA();
}
}else if(returnType.equals(B.class)){
for(T singleElement : data){
((B)singleElement).methodInsideB();
}
}
}
I realize that this is NOT the way to do this. I just cannot find a proper way without all the if-else and the unchecked casting.
Based on the assumption that your classes are implementing TransferableData interface, you can add a method invokeMethod() which has void as return type. For below solution to work, you should have same method signature in all the classes.
public interface TransferableData {
void invokeMethod();
}
public class A implements TransferableData {
#Override
public void invokeMethod() {
//Your business logic A
}
}
public class B implements TransferableData {
#Override
public void invokeMethod() {
//Your business logic for B
}
}
Iterate over your list of objects as (No need to pass dataType to this method):
private <T extends TransferableData> String doSomething(List<T> data) {
for(T object: data){
object.invokeMethod();
}
//return the string according to your business logic.
}
As per the comment from Mena you can try something like this,
public interface TransferableData {
void genericInvoker();
}
public class A implements TransferableData {
#Override
public void genericInvoker() {
methodInsideA();
}
void methodInsideA(){
// Do specific thing to A
}
}
public class B implements TransferableData {
#Override
public void genericInvoker() {
methodInsideB();
}
void methodInsideB(){
// Do specific thing to B
}
}
Then you can invoke the methods as follows:
private <T extends TransferableData> String doSomething(List<T> data, Class<T> dataType){
for(T singleElement : data){
singleElement.genericInvoker();
}
//Don't forget to return a String from this method
}

How can I implement my own generic collection in java?

I have an inteface 'MyCollection' with just two methods : addAll and containsAll which take as a parameter a generic collection. How can I effectively implement these methods in my class so that they would work on any type of collection.
This is what I've done so far :
The interface:
interface MyCollection<T extends Collection> {
boolean containsAll(T c);
boolean addAll(T c);
}
My class where I implement the methods:
public class MyPersonalCollection<E extends Collection> implements MyCollection {
private E myCollection;
public MyPersonalCollection(E myCollection) {
this.myCollection = myCollection;
}
public boolean containsAll(Collection c) {
return myCollection != null && myCollection.containsAll(c);
}
public boolean addAll(Collection c) {
return myCollection != null && myCollection.addAll(c);
}
}
And the tests:
#Test
public void testIfNewCollectionCanBeAdded() {
ArrayList<String> input = new ArrayList<>();
MyPersonalCollection<ArrayList<String>> myCollection = new MyPersonalCollection<>(input);
input.add("first");
input.add("secon");
input.add("third");
assertTrue(myCollection.addAll(input));
}
#Test
public void testIfMyCollectionContainsAnotherCollection() {
LinkedList<String> list = new LinkedList<>();
MyPersonalCollection<LinkedList<String>> myCollection = new MyPersonalCollection<>(list);
list.add("bacon");
list.add("tuna");
list.add("steak");
assertTrue(myCollection.addAll(list));
}
I also get a warning : Unchecked call to 'containsAll(Collection) as a member of raw type 'Java.Util.Collection" in my class when I call the methods containsAll() and addAll().
So how can I tackle this problem ? Many thanks in advance !
Both E and T extend Collection, but you want to treat a Collection as T in MyCollection in this line:
return myCollection != null && myCollection.containsAll(c);
Which can be wrong because every Collection is not from type T.
Anyway if you are sure that this type casting is safe, simply ignore it and use
#SuppressWarnings("unchecked")
to suppress that warning.
The problem is that you have to define 2 generic:
C for the kind of collection
E for the content of the collection
I fixed your code, now there is no warnings
interface MyCollection<C> {
boolean containsAll(C c);
boolean addAll(C c);
}
public class MyPersonalCollection<E, C extends Collection<E>>
implements MyCollection<C> {
private C collection;
public MyPersonalCollection(C myCollection) {
this.collection = myCollection;
}
public boolean containsAll(C c) {
return collection != null && collection.containsAll(c);
}
public boolean addAll(C c) {
return collection != null && collection.addAll(c);
}
}
You can use your class in the test like this:
MyPersonalCollection<String, LinkedList<String>> myCollection =
new MyPersonalCollection<String, LinkedList<String>>(list);

Categories