【问题标题】:Enum relating to a string array that was read from file与从文件中读取的字符串数组相关的枚举
【发布时间】:2013-06-18 13:32:26
【问题描述】:

我收到了一个工作代码(Java 1.7),它执行以下操作: 将文件中的字符串数组(血液测试名称列表)加载到字符串数组成员中(使用 Properties 和 FileInputStream)。该文件可以更改字符串,但含义保持不变(例如:一个测试可以称为“abc”,而在另一个运行中它称为“zzz”)。我有一个枚举测试名称的枚举类。枚举字符串与输入的字符串不同(因为后者可以改变)。

文件 bloodtest.names 包含:

bloodTestNames=abc;def;123;

代码:

public enum BloodTestNames {
AAA,BBB,CCC;    
}

Properties props = new Properties();
FileInputStream fis = new FileInputStream("bloodtest.names");
props.load(fis);
String testName[]=props.getProperty("bloodTestNames").toString().split(";");

现在问题: 问题一: 当我知道测试名称时,我需要返回文件中设置的字符串(例如:为值 BBB 返回“def”)。这样做最好的是什么?

我想出的最好方法是:

return testName[BloodTestNames.BBB.ordinal()]

问题 2:如果 BBB 在编译时未知 - 我如何实现相同的目标?

三点: * 我是 C 的老手,但 Java 的新手。欢迎任何做和不做的事。假设我的 Java 知识为零。 * 我不完全重构是这里需要的。 * 我可能忘记提及重要细节,请询问,我会感觉到缺失的空白

【问题讨论】:

  • 鉴于值是从文件中读取的,选项的数量会改变,但枚举值将保持不变。在这种情况下,您不应该枚举。你能告诉我们你为什么选择枚举吗?
  • "(使用 Properties 和 FileInputStream)"
  • @sidgate:枚举的选择是这样的,无论实际字符串是什么,代码都是相同的。你将如何解决这个问题(有或没有枚举)。主要目标是使代码具有可读性和可维护性。
  • @fge: 我用了一个我看到的例子(现在好像找不到了...)
  • @DrorCohen, I owe you one(对于“一”的任何定义)

标签: java arrays string enums


【解决方案1】:

我首先假设您确实需要枚举常量来为这个用例建模,因为您需要为每种血液测试执行某种特定的代码(否则,一组简单的字符串就足够了,而且更灵活,因为您不需要预先知道测试的数量或关心他们的名字)。


Q1:由于 Java 枚举不仅仅是一个值序列,因此您可以充分利用其面向对象的特性。

public enum BloodTest {
    AAA, BBB, CCC;

    private static String[] names;

    public static void setNames(String[] names) {
        if (BloodTest.names != null)
            throw new IllegalStateException("You can only set the names once");
        if (names.length != values().length)
            throw new IllegalArgumentException("Wrong number of names");
        BloodTest.names = names;
    }

    @Override
    public String toString() {
        return names[ordinal()];
    }
}

现在您需要做的就是通过调用BloodTest.setNames(namesFromConfiguration) 来初始化您的枚举,然后您可以通过调用标准的toString() 方法来获取每个常量的字符串表示形式:BloodTest.BBB.toString()

由于最初的假设是您对每种测试类型都有一些特定的逻辑,我建议逻辑(以及所需的属性)也将封装在枚举本身或枚举常量中;例如:

public enum BloodTest {
    AAA(10) {
        @Override
        public boolean isRequired(MedicalRecord medicalRecord) {
            return medicalRecord.includes("someDisease");
        }
    },
    BBB(15) {
        @Override
        public boolean isRequired(MedicalRecord medicalRecord) {
            return ! medicalRecord.hasTakenBloodTestsLately();
        }
    },
    CCC(20) { // ... also implements the abstract method and so on

    private final int threshold;

    private BloodTest(int threshold) {
        this.threshold = threshold;
    }

    public boolean hasPassed(int value) {
        return value <= threshold;
    }

    public abstract boolean isRequired(MedicalRecord medicalRecord);

    // ... same as above
}

现在,一旦您获得对某个 BloodTest 的引用,您可以通过调用相应的方法而不使用 switching 并让逻辑散布在客户端代码周围来检查该特定测试是否通过:

BloodTest bloodTest = BloodTest.valueOf(someString); // someString can be "AAA", "BBB" or "CCC"
// no matter which constant this is, you use it as an object and rely on polymorphism
if (bloodTest.hasPassed(someValue)) { // ... do something

Q2:您的问题 2 是我对您实际需要枚举的初步假设。如果您可能需要动态处理您还不知道的血液测试,那么您不能使用枚举。

换句话说,如果您的代码没有任何 switchif/else if 块来处理每次血液测试,那么枚举对于您的用例来说是一个非常糟糕的选择。

但是,如果确实如此,那么我建议重构代码以将逻辑包含在枚举本身中,如上例所示,而不是在 switch/if 块中;此外,如果您的 switch 有一个 default 案例(或者您的 if 有一个最终的 else 块),这仍然可以在枚举本身中建模,例如通过添加一个 DEFAULT 常量作为后备.

【讨论】:

  • 尝试使用您的答案让我遇到很多“错误:变量 XXX 可能尚未初始化”。第一个错误是当我调用 BLoodTest.setNames() 时。其他地方是我尝试循环使用它的地方:for(BloodTests bt: btn.values()) - 我在做什么新手错误?
  • 请忽略我的最后一条评论...尝试使用您的答案让我收到“错误:变量 XXX 可能尚未初始化”。当我第一次调用 BloodTest.setNames() 时。我是否需要声明一个“statuc public BloodTest bt”然后调用 bt.setNames()?我想既然它是一个 Enum 类,我可以在我的初始化代码中调用 BLoodTest.setNames()...
  • 不,因为它是一个静态方法,你需要在枚举类型本身上调用它:BloodTest.setNames(someNames)。这会引发错误吗?我的第一个猜测是您的局部变量 someNames 未初始化;例如,如果您在 try 块中分配它并尝试在该块之外使用它。
【解决方案2】:

使整个设置驱动:添加一个 statuc 方法来加载哪些字符串映射到哪些枚举的设置,并添加一个使用这些设置的工厂方法:

public enum BloodTestNames {
    AAA,BBB,CCC; 
    private static Map<String, BloodTestNames> map = new HashMap<String, BloodTestNames>();

    public static void addAlias(String alias, String name) {
        map.put(alias, valueOf(name));
    }

    public static BloodTestNames getByAluas(String alias) {
        if (map.containsKey(alias))
            return map.get(alias);
        // own name assumed to be mapped
        return valueOf(alias);
    }
}

在启动时,根据一些设置文件反复调用BloodTestNames.addAlias()来加载映射。

当您读取保存的文件时,使用BloodTestNames.getByAlias() 返回给定字符串值的枚举。


你最好用单数命名你的类,并去掉“名称”,即BloodTest - 为每个枚举命名类(所有枚举都有一个“名称”,它是编码的实例名称)。

【讨论】:

    【解决方案3】:

    我的枚举类之一的简短摘录:

    public enum TypesStructurelsE {
        SOURCE("SRC"),
        COLONNE("COL");
    
        private String code;
    
        TypesStructurelsE(final String code1) {
            code = code1;
        }
    
    
        /** @return String */
        public String getCode() {
            return code;
        }
    
        public void setCode(final String newCode) {
            code = newCode;
        }
    }
    

    。 .在其他班级

    if(TypesStructurelsE.SOURCE.getCode().equal(testName[i])){ // can be "COL" or "SRC" 
    //
    ;
    }
    

    ...改变值:

    TypesStructurelsE.SOURCE.setCode("SOURCE_NEW");
    

    因此,如果您的属性文件发生更改,您只需使用新符号 (SRC --> SOURCE) 进行编译即可

    【讨论】:

    • 有没有不重新编译的解决方案?不同的文件代表不同的客户端,所以我希望能够使用相同的代码和不同的参数文件
    • 没错,我忘记了,我的回复已更改 - 谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-07
    • 1970-01-01
    • 2017-10-07
    • 2021-01-21
    相关资源
    最近更新 更多