设计查询 - 使用 Java 泛型进行代码重复

Design query - Using Java Generics for code duplication

问题

我正在尝试设计两种过滤列表的方法

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
}

问题是这两种方法都有重复的代码,除了过滤部分,我在 TypeA 和 TypeB 中访问不同的参数。

到目前为止我尝试过的事情


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

需要帮助

我真的是设计新手。想了解我的推理是否正确。还寻找有关解决此问题的替代最佳方法的建议。提前致谢。

假设您有这 2 TypeX 没有继承的接口 link 并且具有相同的方法签名。

interface TypeA {
    String methodFromA();
}

interface TypeB {
    String methodFromB();
}

您可以声明一个枚举,它知道必须为每个 TypeX 接口调用哪个方法。


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


最后,在您的过滤器方法中,您只需要使用 TypeX class 恢复枚举实例并在其上调用适当的方法。

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

适当的结构不反射,不使用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
}

这正是 Predicate<T> 功能接口旨在解决的问题类型。

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()
            ;
   
   }

}