我如何删除Java中的枚举和泛型的重复源代码?

h5qlskok  于 2023-01-11  发布在  Java
关注(0)|答案(2)|浏览(122)

我想删除下面重复的源代码。

public static List<Map<String, Object>> getTransmissionTypeList() {

    List<Map<String, Object>> transmissionList = new ArrayList<>();

    for(EstimateTransmissionType transmissionType : EstimateTransmissionType.values()) {
        Map<String, Object> transmission = new HashMap<>();
        transmission.put("code", transmissionType.getCode());
        transmission.put("value", transmissionType.getValue());
        transmission.put("name", transmissionType.getName());
        transmissionList.add(transmission);
    }

    return transmissionList;

}

public static List<Map<String, Object>> getFuelTypeList() {

    List<Map<String, Object>> fuelList = new ArrayList<>();

    for(EstimateFuelType fuelType : EstimateFuelType.values()) {
        Map<String, Object> transmission = new HashMap<>();
        transmission.put("code", fuelType.getCode());
        transmission.put("value", fuelType.getValue());
        transmission.put("name", fuelType.getName());
        fuelList.add(transmission);
    }

    return fuelList;

}

对于EstimateTransmissionTypeEstimateFuelType,这两个枚举内部的结构完全相同。
x一个一个一个一个x一个一个二个x
我真的很想提高我的源代码质量,但我不知道从哪里开始。有人能给我一个好的灵感或明确的答案吗?

ffx8fchx

ffx8fchx1#

在这里,我们可以使用新老智慧,即偏好组合而非继承。我们可以在委托类中做所有常见的事情:

import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

public class EnumDemo {
  interface AttributeProvider {
    EstimateAttributes getAttributes();
  }
  
  public enum EstimateTransmissionType implements AttributeProvider {
    AUTO("오토(A/T)", "A001", "001"),
    MANUAL("수동(M/T)", "A002", "002"),
    ETC("기타", "A999", "999");

    private final final EstimateAttributes attributes;
    
    EstimateTransmissionType(String name, String value, String code) {
      this.attributes = new EstimateAttributes(name, value, code);
    }

    @Override
    public EstimateAttributes getAttributes() {
      return attributes;
    }
  }
  
  public enum EstimateFuelType implements AttributeProvider {
    GASOLINE("가솔린", "A001", "001"),
    DIESEL("디젤", "A002", "002"),
    LPG_GENERAL("LPG(일반인 구입)", "A003", "003"),
    LPG_ALL("LPG", "A004", "004"),
    HYBRID("가솔린+전기", "A005", "005"),
    ELECTRONIC("전기", "A009", "009"),
    ETC("기타", "A999", "999");

    private final EstimateAttributes attributes;

    EstimateFuelType(String name, String value, String code) {
      attributes = new EstimateAttributes(name, value, code);
    }

    @Override
    public EstimateAttributes getAttributes() {
      return attributes;
    }
  }
  
  static class EstimateAttributes {
    private final String name;
    private final String value;
    private final String code;

    public EstimateAttributes(String name, String value, String code) {
      this.name = name;
      this.value = value;
      this.code = code;
    }
    
    public Map<String, Object> asMap() {
     Map<String, Object> map = new HashMap<>();
     map.put("name", name);
     map.put("value", value);
     map.put("code", code);
     return map;
    }

     // Add getters if needed.
  }
  
  public static <E extends Enum<E> & AttributeProvider> 
      List<Map<String, Object>> getAttributeMaps(Class<E> enumClass) {
    return Arrays.stream(enumClass.getEnumConstants())
        .map(enumValue -> enumValue.getAttributes().asMap())
        .collect(Collectors.toList());
  }
}

现在我们可以说EnumDemo.getAttributeMaps(EstimateTransmissionType.class)EnumDemo.getAttributeMaps(EstimateFuelType.class)

chhqkbe1

chhqkbe12#

此答案相关且有帮助--https://stackoverflow.com/a/70596442/10118965
但这里有一个更直接的答案。简而言之,使用接口、枚举和泛型可以让您更容易地进行整合。
首先,创建一个包含所有相关函数的接口,我将其命名为ParentInterface,在本例中,您需要::getName::getValue::getCode
接下来,你需要在接口内部创建一个方法,返回枚举的值,这是一个更复杂的步骤,因为它要求你向泛型世界迈出相当大的一步,结合枚举。
简而言之,如果我们查看枚举的JavaDoc,我们会看到以下内容。
https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Enum.html
Class Enum<E extends Enum<E>>
所以简而言之,如果我们想说某个类型E是枚举,那么我们必须这样说。
E extends Enum<E>
好了,现在我们知道了如何说参数化类型必须是一个枚举。我们这样做的原因是因为我们实际上想得到***枚举的值***。我们不能在没有首先确定我们有一个枚举的情况下做到这一点。
但现在我们知道了,我们可以写一个类似这样的方法。

public static <E extends Enum<E>> List<E> values(final Class<E> clazz)
{

   return Arrays.asList(clazz.getEnumConstants());

}

这个方法说明我们作为参数接收的clazz必须是枚举类型,一旦它证明了这一点,我们就可以安全地调用Class::getEnumConstants方法,并确保我们将得到我们所期望的结果。
所以,现在我们已经写了静态方法,接下来我们在接口上放一个参数化类型。特别地,我们要确保这个接口只引用枚举。
我们将以下参数化类型添加到接口中。
<E extends Enum<E>>
最后,我们稍微修改一下静态方法,以确保它只用于为实现我们接口的枚举生成枚举值,也许你不希望这样的限制,但如果你想这样做,这里是你要添加的。
& ParentInterface<E>
这样,我们就完成了。这是我们的界面应该是大致的样子。

public interface ParentInterface<E extends Enum<E>> // pick a better name
      {
      
         String getName();
         String getValue();
         String getCode();
         
         public static <E extends Enum<E> & ParentInterface<E>> List<E> values(final Class<E> clazz)
         {
         
            return Arrays.asList(clazz.getEnumConstants());
         
         }
      
      }

接下来,我们需要确保所有枚举都实现了这个新接口,这很容易做到。
x1米11米1x
public enum EstimateTransmissionType implements ParentInterface<EstimateTransmissionType>
枚举的实际内部不应该改变,你所做的只是改变外部。
最后,让我们重新编写方法,以处理这个全新的接口和重新编写的枚举。
让我们从构建::getFuelTypeList方法开始,这意味着我们从一个重复的,但是重命名的方法开始。

public static List<Map<String, Object>> getGenericTypeList() {
      
         List<Map<String, Object>> fuelList = new ArrayList<>();
      
         for(EstimateFuelType fuelType : EstimateFuelType.values()) {
            Map<String, Object> transmission = new HashMap<>();
            transmission.put("code", fuelType.getCode());
            transmission.put("value", fuelType.getValue());
            transmission.put("name", fuelType.getName());
            fuelList.add(transmission);
         }
      
         return fuelList;
      
      }

除了方法名之外,此方法与::getFuelTypeList完全相同。
让我们从修改方法签名开始,让它包含我们创建的泛型,更具体地说,让我们把这个方法引入到接口的概念中。
public static <E extends ParentInterface<E>> List<Map<String, Object>> getGenericTypeList()
但是如果我们这样做的话,编译器就会抱怨我们,说如果我们想让E扩展我们的接口,它也必须扩展Enum<E>,否则就会破坏我们的接口规则。它说 “任何扩展我的东西都必须在类型参数中有一个枚举,这个枚举也扩展了我”。我们在前面将类型参数放在接口上时制定了这个规则。
所以,为了克服这个问题,我们必须指定我们的类型参数也是一个枚举。这很容易改变。
public static <E extends Enum<E> & ParentInterface<E>> List<Map<String, Object>> getGenericTypeList()
现在,编译器很高兴了。
因此,由于这个变化,我们的方法现在知道一个名为E的类型,这个类型是枚举的子类型,也是我们创建的接口的子类型。
既然方法已经知道了我们的类型,让我们实际使用它。

public static <E extends Enum<E> & ParentInterface<E>> List<Map<String, Object>> getGenericTypeList() {
      
         List<Map<String, Object>> fuelList = new ArrayList<>();
      
         for(EstimateFuelType fuelType : EstimateFuelType.values()) {
            Map<String, Object> transmission = new HashMap<>();
            transmission.put("code", fuelType.getCode());
            transmission.put("value", fuelType.getValue());
            transmission.put("name", fuelType.getName());
            fuelList.add(transmission);
         }
      
         return fuelList;
      
      }

显然,所有的东西都是硬编码的,所以让我们从改变它开始。

public static <E extends Enum<E> & ParentInterface<E>> List<Map<String, Object>> getGenericTypeList() {
      
         List<Map<String, Object>> fuelList = new ArrayList<>();
      
         for(E fuelType : E.values()) {
            Map<String, Object> transmission = new HashMap<>();
            transmission.put("code", fuelType.getCode());
            transmission.put("value", fuelType.getValue());
            transmission.put("name", fuelType.getName());
            fuelList.add(transmission);
         }
      
         return fuelList;
      
      }

这几乎可以工作,但是编译器很不高兴,说它不知道在哪里可以找到E::values方法。令人恼火的是,Java不允许我们重写静态方法,所以这个解决方案不得不变得不必要的复杂。幸运的是,我们已经在前面完成了复杂的部分。与其尝试使用未知的E::values方法,为什么我们不使用前面创建的静态方法呢?
然而,为了使用这个方法,我们需要提供一个Class<E>,简单地说,我们还要修改我们的方法以接受一个Class<E>,并且还要替换那个刚刚抛出错误的未知方法。

public static <E extends Enum<E> & ParentInterface<E>> List<Map<String, Object>> getGenericTypeList(final Class<E> clazz) {
      
         List<Map<String, Object>> fuelList = new ArrayList<>();
      
         for(E fuelType : ParentInterface.values(clazz)) {
            Map<String, Object> transmission = new HashMap<>();
            transmission.put("code", fuelType.getCode());
            transmission.put("value", fuelType.getValue());
            transmission.put("name", fuelType.getName());
            fuelList.add(transmission);
         }
      
         return fuelList;
      
      }

然后应该就是这样了。现在你可以删除不必要的方法,这些方法只对单个枚举有效。下面是完整的解决方案,包括一些重构、重命名和一般清理。

import java.util.Arrays;
import java.util.List;
import java.util.Map;

   public class SOQ_20230108
   {
   
      public static void main(String[] args)
      {
      
         System.out.println(getParentTypeList(EstimateFuelType.class));
         System.out.println(getParentTypeList(EstimateTransmissionType.class));
      
      }
   
      public interface ParentInterface<E extends Enum<E>> // pick a better name
      {
      
         String getName();
         String getValue();
         String getCode();
         
         public static <E extends Enum<E>> List<E> values(final Class<E> clazz)
         {
         
            return Arrays.asList(clazz.getEnumConstants());
         
         }
      
      }
   
      public enum EstimateFuelType implements ParentInterface<EstimateFuelType>
      {
      
         GASOLINE("가솔린", "A001", "001"),
         DIESEL("디젤", "A002", "002"),
         LPG_GENERAL("LPG(일반인 구입)", "A003", "003"),
         LPG_ALL("LPG", "A004", "004"),
         HYBRID("가솔린+전기", "A005", "005"),
         ELECTRONIC("전기", "A009", "009"),
         ETC("기타", "A999", "999");
      
         final String name;
         final String value;
         final String code;
      
         EstimateFuelType(String name, String value, String code) 
         {
            
            this.name = name;
            this.value = value;
            this.code = code;
         
         }
      
         public String getName() 
         {
            
            return name;
         
         }
      
         public String getValue() 
         {
            
            return value;
         
         }
      
         public String getCode() 
         {
            
            return code;
         
         }
      
      }
   
      public enum EstimateTransmissionType implements ParentInterface<EstimateTransmissionType>
      {
      
         AUTO("오토(A/T)", "A001", "001"),
         MANUAL("수동(M/T)", "A002", "002"),
         ETC("기타", "A999", "999");
      
         final String name;
         final String value;
         final String code;
      
         EstimateTransmissionType(String name, String value, String code) 
         {
            
            this.name = name;
            this.value = value;
            this.code = code;
         
         }
      
         public String getName() 
         {
            
            return name;
         
         }
      
         public String getValue() 
         {
            
            return value;
         
         }
      
         public String getCode() 
         {
            
            return code;
         
         }
      
      }
   
      public static <E extends Enum<E> & ParentInterface<E>> List<Map<String, Object>> getParentTypeList(Class<E> clazz) {
      
         final List<Map<String, Object>> typeList = new ArrayList<>();
      
         for(final E type : ParentInterface.values(clazz)) {
            final Map<String, Object> typeMap = new HashMap<>();
            typeMap.put("code", type.getCode());
            typeMap.put("value", type.getValue());
            typeMap.put("name", type.getName());
            typeList.add(typeMap);
         }
      
         return typeList;
      
      }
   
   }

这个完整的示例有一个main方法,因此您可以自己运行它并查看它是否工作。
最后,如果你决定让这个方法为你将来添加的其他枚举工作,这是非常容易做到的。只要确保你添加的每个枚举实现你的接口,并且那个接口的参数化类型应该是枚举名本身。如果这没有意义,只要遵循与其他枚举相同的格式,它应该工作得很好。

相关问题