28379
`Profile2.java`をコンパイル時に一番下のようなエラーが出ます。  
自分としてはクラスのインスタンス時に引数に沢山書く代わりに、連想配列を渡して処理させようと思いました。  
しかし、２つの問題が出てきました。  
・継承したProfileのnameを設定出来ない  
・HashMapのkeySet()部分が上手くいかない  
どのように書き換えればいいのでしょうか？  
よろしくお願いします。

Profile.java

```

    /** 
     * Profile class
     * @auther yyy
     */
    public class Profile {
    
        /** name */
        private String name;
    
        /**
         * constructor
         * @param name name
         */
        Profile(String name) {  // constructor
            this.name = name;
        }
    
    }
    
```

MyProfile2.java

```

    import java.util.Map;
    import java.util.HashMap;
    
    
    /*  Main.java内抜粋
        Map<String, Object> myprof = new HashMap<String, Object>(){
            {
                put("name", "John Lennon");
                put("phone_number", "+81-90-0000-0000");
                put("email", "lennonkun@gmail.com");
                put("occupation", new String[]{"Programmer", "System Engineer"});
                put("language", new String[]{"Japanese", "Spanish", "English"});
                put("hobby", new String[]{"Traveling", "fishing", "photography"});
            }
        };
    
        MyProfile2 me2 = new MyProfile2(myprof);
    */
    
    public class MyProfile2 extends Profile {
        private String email;
        private String phone_number;
        private String[] occupation;
        private String[] language;
        private String[] hobby;
    
        public MyProfile2(HashMap myprof) {
            for(String key: myprof.keySet()) {
                switch(key) {
                    case "name": super(name);
                    case "email": this.email = (String)myprof.get(key);
                    case "phone_number": this.phone_number = (String)myprof.get(key);
                    case "occupation": this.occupation = (String[])myprof.get(key);
                    case "language": this.language = (String[])myprof.get(key);
                    case "hobby": this.hobby = (String[])myprof.get(key);
                }
            }
        }
    
    }
    
```

以下のエラーが出ます。

```

    MyProfile2.java:11: エラー: クラス Profileのコンストラクタ Profileは指定された型
    に適用できません。
            public MyProfile2(HashMap myprof) {
                                              ^
      期待値: String
      検出値: 引数がありません
      理由: 実引数リストと仮引数リストの長さが異なります
    
    MyProfile2.java:12: エラー: 不適合な型: ObjectをStringに変換できません:
                    for(String key: myprof.keySet()) {
                                                 ^
    MyProfile2.java:14: エラー: superの呼出しはコンストラクタの先頭文である必要があ
    ります
                                    case "name": super(name);
                                                      ^
    エラー3個
    
```
CC BY-SA 3.0
2016-08-19T12:11:45.077
28377
2016-08-27T11:05:30.673
18525
question
java
Javaで連想配列をクラスのインスタンス時に指定して、コンストラクタ内でそれぞれを設定したいのですが
public MyProfile2(HashMap myprof) {  
に修正。

MyProfile2.java:14:は、エラーの通り、親クラスのコンストラクタ呼び出しは自クラスのコンストラクタの先頭な必要があるのというのと、  
nameキーが必須である点がMyProfile2  
を使う人から見えにくい問題があるので…  
public static MyProfile2(String name, HashMap myprof)  
というような生成メソッドにしてみてはいかがでしょうか。

以下、追記。

好みをいうと、こういった場合はチェーンメソッドにするのが好みです。

```

    public MyProfile2(String name) {
        super(name);
    }
    public MyProfile2 email(String...ems){
        this.email = ems;
        return this;
    }
    public MyProfile2 tel(String...tls){
        this.tel = tls;
        return this;
    }
    
    //使う側
    new MyProfile2("ken")
      .email("gmail.com", "a@hotmail")
      .tel("09088888888");
    
```
CC BY-SA 3.0
2016-08-19T12:59:24.980
28378
2016-08-27T11:05:30.673
2016-08-27T11:05:30.673
15185
12233
28377
answer
> ・継承したProfileのnameを設定出来ない

コンストラクタ内で`super();`は最初に呼び出す必要があります。  
コンストラクタの1行目に記述してください。

もしくは`Profile`をinterfaceにする方法があります。

```

    interface Profile {
        String getName();
    }
    
    class MyProfile implements Profile {
        private String name;
    
        public MyProfile(Map<String, Object> myprof) {
            this.name = (String) myprof.get("name");
        }
    
        @Override
        public String getName() {
            return name;
        }
    }
    
```

> ・HashMapのkeySet()部分が上手くいかない

コンストラクタ引数の`HashMap`に型が指定されていないため、`HashMap<Object, Object>`として扱われています。  
`HashMap<String, Object>`としてください。

その他、気になった点です。

  * `entrySet()`を使用することでMapのkeyとvalueのペア(`Entry`)を取得できます  
keyとvalueの両方を使用する場合、こちらを使用したほうが良いです

  * `HashMap`以外の`Map`も許容するように`Map<String, Object>`とした方がよいです
  * 各caseでbreakが必要な気がします

```

    import java.util.Map;
    import java.util.Map.Entry;
    
    public class MyProfile2 extends Profile {
        private String email;
        private String phone_number;
        private String[] occupation;
        private String[] language;
        private String[] hobby;
    
        public MyProfile2(Map<String, Object> myprof) {
            super((String)myprof.get("name"));
    
            for (Entry<String, Object> entry : myprof.entrySet()) {
                switch (entry.getKey()) {
                    case "email":
                        this.email = (String)entry.getValue();
                        break;
                    case "phone_number":
                        this.phone_number = (String)entry.getValue();
                        break;
                    case "occupation":
                        this.occupation = (String[])entry.getValue();
                        break;
                    case "language":
                        this.language = (String[])entry.getValue();
                        break;
                    case "hobby":
                        this.hobby = (String[])entry.getValue();
                        break;
                }
            }
        }
    
    }
    
```

for文を使用せずに下記のように書いても良さそうですが。

```

    public MyProfile2(Map<String, Object> myprof) {
        super((String)myprof.get("name"));
    
        this.email        = (String)   myprof.get("email");
        this.phone_number = (String)   myprof.get("phone_number");
        this.occupation   = (String[]) myprof.get("occupation");
        this.language     = (String[]) myprof.get("language");
        this.hobby        = (String[]) myprof.get("hobby");
    }
    
```

この設計の良し悪しについては本質問の範疇を超えるため回答を差し控えます。
CC BY-SA 3.0
2016-08-19T12:59:41.310
28379
2016-08-19T14:05:48.060
2016-08-19T14:05:48.060
3068
3068
28377
answer
> 継承したProfileのnameを設定出来ない

エラーにあるとおりです。コンストラクタの先頭で`super`を呼んでください。（そもそも通るかわからないパスに`super`があるのは問題ある気が･･･）

> HashMapのkeySet()部分が上手くいかない

`HashMap<String,Object> myprof`のように型指定を付けてください。

また、他にもエラーの原因となる部分があります。

  1. `switch(式)`の`式`でObjectが使えないこと。
  2. コンストラクタ`MyProfile2`の引数は`HashMap`なのに`main`では`Map`を渡していること。

以下はこれら修正して一応コンパイルが通るようになったコードです。

```

    import java.util.Map;
    import java.util.HashMap;
    
    public class MyProfile2 extends Profile {
        private String email;
        private String phone_number;
        private String[] occupation;
        private String[] language;
        private String[] hobby;
    
        public MyProfile2(Map<String,Object> myprof) {
            super((String)myprof.get("name"));
            this.email = (String)myprof.get("email");
            this.phone_number = (String)myprof.get("phone_number");
            this.occupation = (String[])myprof.get("occupation");
            this.language = (String[])myprof.get("language");
            this.hobby = (String[])myprof.get("hobby");
        }
    }
    
```

どのみちキーがなければ値が入らないので`for`で回す必要もないかと思います。（`get`が`null`を返してくれるので）

それから連想配列で値を渡すことについてですが、これは避けたほうが良いと思います。  
何故なら`name`,`email`等が値を持たないことを許容するにしても、意図的に`null`を渡すのと、キーの文字列を間違えるなどして、意図せずして値が渡されないのでは全く異なるからです。後者はコンストラクタ引数を別々に書く方法であればコンパイル時に検出できますが、連想配列を渡す方法では少なくとも実行時までは分からないからです。
CC BY-SA 3.0
2016-08-19T13:29:53.083
28380
2016-08-19T13:36:54.723
2016-08-19T13:36:54.723
13199
13199
28377
answer