繁体   English   中英

从Java中的构造函数声明和设置私有最终成员变量的正确方法?

[英]Proper way to declare and set a private final member variable from the constructor in Java?

有多种方法可以从构造函数中设置成员变量。 我实际上正在讨论如何正确设置最终成员变量,特别是一个由辅助类加载条目的映射。

public class Base {
    private final Map<String, Command> availableCommands;
    public Base() {
        availableCommands = Helper.loadCommands();  
    }
}

在上面的例子中,helper类看起来像这样:

public class Helper {
    public static Map<String, Command> loadCommands() {
        Map<String, Command> commands = new HashMap<String, Command>();
        commands.put("A", new CommandA());
        commands.put("B", new CommandB());
        commands.put("C", new CommandC());

        return commands;
    }
}

我的想法是,使用方法在构造函数中设置这样的变量是更好的做法。 所以Base类看起来像这样:

public class Base {
    private final Map<String, Command> availableCommands;
    public Base() {
        this.setCommands();  
    }
    private void setCommands() {
        this.availableCommands = Helper.loadCommands();
    }
}

但是现在我无法维护最终修饰符并得到编译器错误(无法设置最终变量)

另一种方法是:

public class Base {
    private final Map<String, Command> availableCommands = new HashMap<String, Command>();
    public Base() {
        this.setCommands();
    }
    private void setCommands() {
        Helper.loadCommands(availableCommands);
    }
}

但在这种情况下,Helper类中的方法将更改为:

public static void loadCommands(Map<String, Command> commands) {
    commands.put("A", new CommandA());
    commands.put("B", new CommandB());
    commands.put("C", new CommandC());
}

所以区别在于我在哪里用new HashMap<String, Command>();创建一个新的地图new HashMap<String, Command>(); 我的主要问题是,是否有推荐的方法来实现这一点,因为部分功能来自这个Helper的静态方法,作为一种用条目加载实际地图的方法?

我在Base类或Helper类中创建新地图吗? 在这两种情况下,Helper都会执行实际加载,Base对包含具体命令的地图的引用将是私有和最终的。

除了我正在考虑的选项之外,还有其他更优雅的方法吗?

根据你的第一个代码片段,帮助类创建地图对我来说似乎是完全合理的。 您在构造函数设置变量 - 我看不到问题。

正如打哈欠所说,让地图不可变在这里会很好看,但除此之外,我只是使用第一段代码。

(我认为在现实生活中,这确实需要是一个实例变量,而不是静态变量,顺便说一下?)

如果您希望此类地图不可变,请查看Google Collection API 引用链接的文档:

static final ImmutableMap<String, Integer> WORD_TO_INT =
       new ImmutableMap.Builder<String, Integer>()
           .put("one", 1)
           .put("two", 2)
           .put("three", 3)
           .build();

您是否考虑使用类似于Effective Java 2nd ed中的Builder模式

您可以在一个地方捕获所有地图构造逻辑(因此您不需要维护2个单独的类)。 Base将如下所示:

public class Base {

    private final Map<String, Command> commands;

    private Base(Builder b) {
        commands = b.commands;
    }

    public static class Builder() {

        private final Map<String, Command> commands;

        public Builder() {
            commands = new HashMap<String, Command>();
        }

        public Builder addCommand(String name, Command c) {
            commands.put(name, c);
            return this;
        }

        public Base build() {
            return new Base(this);
        }
    }
}

Base的客户端现在可以像这样工作:

Base b = new Base.Builder().addCommand("c1", c1).addCommand("c2", c2).build();

Upshot是客户端类不需要知道他们需要构建一个Map,你实际上可以用1行构建它。 缺点是Base无法扩展,因为构造函数现在是私有的(也许你想要那个,也许你不需要)。

编辑:在build()中有一个goof我在那里传递命令而不是像我最初想要的 EDIT2:Mistakenly调用add而不是放入Base.Builder.addCommand

  1. 如果你想让它不可变,你不需要使用第三方API,你可以使用: java.util.Collections.unmodifiableMap(Map m)

  2. 最常见的方法是:

public class Base {
private final Map availableCommands;
public Base(){
  availableCommands=new HashMap(); // or any other kind of map that you wish to load
  availableCommands = Helper.loadCommands(availableCommands);  
 }
}

你为什么不这样做呢

private final Map<String, Command> availableCommands = Helper.loadCommands();  

我个人会将Helper类重命名为CommandHolder:

public class CommandHolder {
    private static Map<String, Command> availableCommands;
    private static CommandHolder instance;

    private CommandHolder{}
    public static synchronized Map<String, Command> getCommandMap() {
        if (instance == null) {
            instance = new CommandHolder();
            instance.load();
        }
        return availableCommands
    }
    private void load() {
        ...
    }
}

同步以确保加载仅发生一次。 没有getCommand,因为那个必须同步,每次查找都会更昂贵。 我假设地图是只读的,否则你无论如何都需要在多线程环境中使用synchronizedMap。

你也可以使用双支撑初始化,虽然你认为它是否更干净可能是一个品味的问题,但至少有一个好处,所有的初始化代码在一个地方:

public class Base {
    public final Map< String, Command > availableCommands;

    public Base() {
        availableCommands = Collections.unmodifiableMap( new HashMap() {
            {
                put( "A", new CommandA() );
                put( "B", new CommandB() );
            }
        } );
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM