加载属性并注入最终变量 – Java

huangapple go评论81阅读模式
英文:

Loading properties and injecting to final variables - Java

问题

我想将属性值加载到一个常量类中。我可以通过在类构造函数中注入该值或者像以下静态初始化一样来实现。

public class MyConstants {
    public final String CONSTANT;

    Myconstants() {
        //读取属性并赋值给this.CONSTANT
    }
}

上述的初始化方法是有效的。但是当我需要在 switch case 语句中使用这个 CONSTANT 时会出现问题。这会导致错误,因为在那时的 CONSTANT 并不是一个常量,而是空白 final 字段。

是否有任何方法可以读取属性文件并将值放入 CONSTANT 中,以便不会出现编译错误?[就像在编译时加载属性并设置]。我正在使用 Spring,是否有任何 Spring 或者 Java 的解决方法来应对这种情况?我的目标是不必每次常量更改时都重新构建。你会如何处理这种情况?

谢谢。

英文:

I want to load property values to a constants class.I can do that by injecting the value in class constructor or static initialization like the following.

public class MyConstants {
    public final String CONSTANT;

    Myconstants() {
        //read property and set to this.CONSTANT
    }
}

Above initialization works fine. But problem arises when I have to use the CONSTANT in a switch case statement. It will give me error because CONSTANT at that point is not constant but blank final field.

Is there any way I can read properties file and put value to CONSTANT so that no compilation error exist? [Like loading properties and setting at compile time itself]. I am using spring and is there any Spring or java workaround for this use case. My intention is to not build for each time a constant change. Also How would you approach to this use case?

Thanks

答案1

得分: 1

在 switch 语句中,字符串是“硬编码”的。在 Java 中,要创建一个 switch/case 语句,其“case”元素在编译时是无法更改的。如果你想在应用程序初始化期间从属性文件加载这些字符串“常量”,它们就不再是常量。

或者,至少就 Java 的基于字符串的 switch/case 的需求而言,它们不再是常量。

相反,使用 java.util.Map(将字符串值映射到表示现在放入 case 块中内容的可运行项或类似内容)或一系列 if/else-if 语句。

编辑:如何使用 Map 来实现这一点。

@FunctionalInterface
public interface CommandProcessor {
    String apply(String cmd);
}

public class CommandHandler {
    private Map<String, CommandProcessor> processors = Map.of(
        "hello", cmd -> "Hello, " + cmd,
        "time", cmd -> "It is " + System.currentTimeMillis() + " past 1970.",
        "capitalize", String::toUpperCase);

    public static void main(String[] args) throws Exception {
        Scanner s = new Scanner(System.in);
        s.useDelimiter("\r?\n");
        CommandProcessor unknownCommand =
            cmd -> "Known commands: " + processors.keySet();

        while (true) {
            String line = s.next();
            String[] parts = line.split("\\s+", 2);
            var processor = processors.getOrDefault(parts[0], unknownCommand);
            String answer = processor.apply(parts.length > 1 ? parts[1] : "");
            System.out.println(answer);
        }
    }
}
英文:

the strings in switch statements are 'hardcoded'. It is literally impossible to make a switch/case statement in java whose 'case' elements are not locked in at compile time. If you want to load those string 'constants' in during app initialization from a properties file, they aren't constant.

Or at least, as far as the needs of java's string-based switch/case is concerned, not constant

Instead, use a java.util.Map of some sort (that maps string values onto runnables or whatnot representing what you're now putting in case blocks), or a bunch of if/else-if statements.

EDIT: How to use a Map to do this.

@FunctionalInterface
public interface CommandProcessor {
    String apply(String cmd);
}

public class CommandHandler {
    private Map&lt;String, CommandProcessor&gt; processors = Map.of(
        &quot;hello&quot;, cmd -&gt; &quot;Hello, &quot; + cmd,
        &quot;time&quot;, cmd -&gt; &quot;It is &quot; + System.currentTimeMillis() + &quot; past 1970.&quot;,
        &quot;capitalize&quot;, String::toUpperCase);

    public static void main(String[] args) throws Exception {
        Scanner s = new Scanner(System.in);
        s.useDelimiter(&quot;\r?\n&quot;);
        CommandProcessor unknownCommand =
            cmd -&gt; &quot;Known commands: &quot; + processors.keySet();

        while (true) {
            String line = s.next();
            String[] parts = line.split(&quot;\\s+&quot;, 2);
            var processor = processors.getOrDefault(parts[0], unknownCommand);
            String answer = processor.apply(parts.length &gt; 1 ? parts[1] : &quot;&quot;);
            System.out.println(answer);
        }
    }
}

答案2

得分: 0

通常,属性文件由“key=value”对组成,可以在运行时加载。环境变量也可以类似地使用。一般情况下,常量是键,值用于影响应用程序逻辑。只要键保持不变,属性值可以根据需要进行更改,而无需重新编译,并且可能值的范围可以在switch语句中使用。例如:

	public static void main(String[] args)
	{
		AppConfigParam<Type1> type1   = new AppConfigParam<>("Param1", Type1::valueOf);
		AppConfigParam<Integer> type2 = new AppConfigParam<>("Param2", Integer::parseInt);
		switch(type1.value().orElseThrow(() -> new IllegalStateException("Missing type"))) { case A: break; }
		switch(type2.value().orElseThrow(() -> new IllegalStateException("Missing type"))) { case 1: break; }
	}

	public enum Type1{ A, B, C }

	private static final class AppConfigParam<T>
	{
		private final String constant;
		private final Function<? super String, T> resolver;
		AppConfigParam(String constant, Function<? super String, T> resolver){ this.constant = constant; this.resolver = resolver; }
		public Optional<T> value(){ return Optional.ofNullable(PARAMS.get(constant)).map(resolver); }
		private static final Map<String, String> PARAMS = System.getenv();
	}

尽管动态设置常量可能不太可能,但可以通过进行两阶段构建过程,在switch语句中使用动态常量。
1)读取属性并生成常量类。
2)编译项目的其余部分。

英文:

Usually a property file consists of 'key=value' pairs which can be loaded at runtime. Environment variables may be used similarly. In general, the constant is the key and the value is used to impact application logic. As long as the key is unchanged, the property value can be changed as needed without any need to recompile and the range of possible values can be used in a switch statement. Ex:

	public static void main(String[] args)
	{
		AppConfigParam&lt;Type1&gt; type1   = new AppConfigParam&lt;&gt;(&quot;Param1&quot;, Type1::valueOf);
		AppConfigParam&lt;Integer&gt; type2 = new AppConfigParam&lt;&gt;(&quot;Param2&quot;, Integer::parseInt);
		switch(type1.value().orElseThrow(() -&gt; new IllegalStateException(&quot;Missing type&quot;))) { case A: break; }
		switch(type2.value().orElseThrow(() -&gt; new IllegalStateException(&quot;Missing type&quot;))) { case 1: break; }
	}

	public enum Type1{ A, B, C }

	private static final class AppConfigParam&lt;T&gt;
	{
		private final String constant;
		private final Function&lt;? super String, T&gt; resolver;
		AppConfigParam(String constant, Function&lt;? super String, T&gt; resolver){ this.constant = constant; this.resolver = resolver; }
		public Optional&lt;T&gt; value(){ return Optional.ofNullable(PARAMS.get(constant)).map(resolver); }
		private static final Map&lt;String, String&gt; PARAMS = System.getenv();
	}

While it will not be possible to dynamically set constants, it may be possible to use dynamic constants in switch statements by having a 2 phase build process.

  1. Read properties and generate constant classes.
  2. Compile rest of project.

huangapple
  • 本文由 发表于 2020年9月18日 22:58:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/63958172.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定