Java의 생성자에서 개인 최종 멤버 변수를 선언하고 설정하는 올바른 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1749588

문제

생성자에서 멤버 변수를 설정하는 방법에는 여러 가지가 있습니다.실제로 최종 멤버 변수, 특히 도우미 클래스에 의해 항목이 로드되는 맵을 올바르게 설정하는 방법에 대해 토론하고 있습니다.

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

위의 예에서 도우미 클래스는 다음과 같습니다.

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

내 생각에는 생성자에서 이러한 변수를 설정하는 방법을 사용하는 것이 더 나은 방법입니다.따라서 기본 클래스는 다음과 같습니다.

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>();? 내 주요 질문은 기능의 일부가 항목과 함께 실제 지도를 로드하는 방법으로 이 도우미의 정적 메서드에서 나온다는 점을 고려할 때 권장되는 방법이 있는지 여부입니다.

Base 클래스나 Helper 클래스에서 새 지도를 생성합니까? 두 경우 모두 Helper는 실제 로딩을 수행하고 구체적인 명령이 포함된 지도에 대한 Base의 참조는 비공개적이고 최종적입니다.

제가 고려하고 있는 옵션 외에 이 작업을 수행할 수 있는 다른 더 우아한 방법이 있을까요?

도움이 되었습니까?

해결책

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

다른 팁

첫 번째 코드 스 니펫에 따라 도우미 클래스가 맵을 만드는 것이 전적으로 합리적입니다. 너 ~이다 생성자에서 변수를 설정 - 문제를 볼 수 없습니다.

Yawn이 말했듯이,지도를 불변으로 만드는 것은 여기에서 좋은 터치가 될 것이지만, 그 외에는 첫 번째 스 니펫에서 코드를 사용합니다.

(실제 생활에서 이것은 실제로 정적 인 인스턴스 변수가되어야한다고 생각합니까?)

그러한지도가 불변이되기를 원한다면 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();

나와 같은 빌더 패턴 사용을 고려해 보셨습니까? 효과적인 Java 2nd ed.?

한 곳에 모든 맵 구성 논리를 캡처 할 수 있습니다 (따라서 유지할 별도의 클래스 2 개가 없음). 베이스는 다음과 같습니다.

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 b = new Base.Builder().addCommand("c1", c1).addCommand("c2", c2).build();

Upshot은 클라이언트 클래스가지도를 작성해야한다는 것을 알 필요가 없으며 본질적으로 1 줄로 모두 빌드 할 수 있다는 것입니다. 단점은 생성자가 현재 비공개이기 때문에베이스를 확장 할 수 없다는 것입니다 (아마도 원할 수도 있습니다.

편집 : build ()에 멍청이가 있었는 edit2 : base.builder.addcommand에 넣는 대신 실수로 Add를 호출

그냥하지 그래

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

?

개인적으로 도우미 클래스의 이름을 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는 동기화되어야하며 각 조회는 더 비쌀 것입니다. 지도가 읽기 전용이라고 가정합니다. 그렇지 않으면 다중 스레드 환경에서 어쨌든 동기화 된 맵이 필요합니다.

이중 중괄호 초기화를 사용할 수도 있습니다. 더 깔끔하다고 생각하는지 여부는 취향의 문제일 수 있지만 적어도 모든 초기화 코드를 한 곳에 보관할 수 있다는 이점이 있습니다.

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 ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top