頭脳一式

人の記憶なんて曖昧なもの。すべての情報を頭に記憶するなんてナンセンス。困ったらここに来ればいいじゃん?というスタンスで最強のナレッジベースを目指すブログ

【Eclipce】アサーション(assert)を有効化する。

assertを有効化する。

アサーション(assert)の機能は、プログラム実行時にはデフォルト設定で無効になっているため、有効にする必要があります。
Eclipceで有効にするには、VM引数に「-enableassertions」又は「-ea」と記述します。 f:id:shiakisudev:20181202163634p:plain

assertの構文

assert文の構文は以下のとおりです。

assert 条件式;          //パターン①
assert 条件式 : 引数;   //パターン②
assert (条件式) : (引数);//パターン③

条件式にはboolean値を返す式を指定します。
結果がFalseの場合にのみ、java.lang.AssertionErrorが発生します。
パターン③のように、条件式や引数を()で括る記述も可能です。

assertの実行①

public static void main(String[] args) {
     int a,b,c;
     a = 100;
     b = 200;
     c = a + b;

     assert c ==30;
}

【実行結果】
Exception in thread "main" java.lang.AssertionError
    at test.testOfAssertion.main(testOfAssertion.java:11)

条件式にはc==30と書きましたが、実際は100+200=300となるため、assertの結果はFlaseとなりjava.lang.AssertionErrorが発生します。

assertの実行②

public static void main(String[] args) {
     int a,b,c;
     a = 100;
     b = 200;
     c = a + b;

     assert c ==30: "30ではありません。";
}

【実行結果】
Exception in thread "main" java.lang.AssertionError: 30ではありません。
    at test.testOfAssertion.main(testOfAssertion.java:11)

引数を与えてみました。そのまま出力されることがわかりました。

【Java】スタック・キューを実現するDeque<E>インターフェース

Deque<E>インターフェースとは

Queueインターフェースを拡張したもので、「Double Ended Queue」両端キューと呼ばれる。
スタックやキューを実現するために用いられる。

特徴としては以下のとおり

  • 両端から要素を挿入・削除することができるデータ構造を実現する。
  • null要素を格納することが出来ない。
  • Indexによる要素へのアクセスをサポートしていない。

つまり、先頭に要素を挿入したり、最後尾に要素を挿入することができる。

代表的な実装クラスは以下のとおり

  • ArrayDeque<E>クラス
  • LinkedList<E>クラス

まとめ

先に後述するArrayDeque<E>クラスの各メソッドの役割を以下にまとめる。

操作 メソッド
リストの先頭に要素を挿入する addFirst()メソッド、push()メソッド
リストの末端に要素を挿入する addLast()メソッド、add()メソッド
リストの先頭の要素を取得する
(取得時に削除する)
pop()メソッド、remove()メソッド、removeFirst()メソッド
リストの末端の要素を取得する
(取得時に削除する)
removeLast()メソッド
リストの先頭の要素を取得する
(取得時に削除しない)
getFirst()メソッド、element()メソッド
リストの末端の要素を取得する
(取得時に削除しない)
getLast()メソッド

ArrayDeque<E>クラスの各メソッド

addメソッド

まずはArrayListと同じようにaddメソッドで要素を挿入して出力してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    System.out.println(deque);
}

【実行結果】
[A, B, C]

ArrayListと同じようにA、B、Cの順番で出力される。

addFirstメソッド

次は、リストの先端に挿入するaddFirstメソッドを試してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    deque.addFirst("D");//addFirstメソッドを実行
    deque.add("E");
    System.out.println(deque);
}

【実行結果】
[D, A, B, C, E]

DEの出力位置に注目。
addFirstメソッドで追加したDは一番先頭へ挿入され、addメソッドで追加したEは後ろへ挿入されることが分かった。

じゃあ、addFirstメソッドを2回使ったらどうなるのか?を次のコードで試してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    deque.addFirst("D");//addFirstメソッドを実行
    deque.add("E");
    deque.add("F");
    deque.addFirst("G");//addFirstメソッドを実行
    System.out.println(deque);
}

【実行結果】
[G, D, A, B, C, E, F]

2回目のaddFirstメソッドで追加したGDよりも前へ挿入されることが分かった。
つまり、addFirstメソッドで挿入した要素は、後続でaddFirstメソッドにより別の要素を挿入しない限りずっと先頭に位置し続ける。
挿入順としては以下になる。
A            //Aが挿入される。
A、B          //最後尾にBが挿入される。
A、B、C        //最後尾にCが挿入される。
D、A、B、C      //先頭にDが挿入される。
D、A、B、C、E    //最後尾にEが挿入される。
D、A、B、C、E、F  //最後尾にFが挿入される。
G、D、A、B、C、E、F //先頭にGが挿入される。

pushメソッド

次は、pushメソッドを試してみる。
pushメソッドaddFirstメソッドと同様に、リストの先端に挿入するメソッドである。

先述のaddFirstメソッドの例2と同じ要素で書いてみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    deque.push("D");//pushメソッドを実行
    deque.add("E");
    deque.add("F");
    deque.push("G");//pushメソッドを実行
    System.out.println(deque);
}

【実行結果】
[G, D, A, B, C, E, F]

addFirstメソッドで実行したときと同じ結果になった。

addLastメソッド

次は、リストの末端に挿入するaddLastメソッドを試してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    deque.addLast("D");//addLastメソッドを実行
    deque.add("E");
    System.out.println(deque);
}

【実行結果】
[A, B, C, D, E]

addLastメソッドDを追加した後に、addメソッドEを追加すると、EDの後ろに挿入される。
つまりaddFirstメソッドのときと違ってaddLastメソッドはずっと末端に位置し続けたりはしないので注意。
すなわち、addLastメソッドaddメソッドと同等の動きをする。
次がaddLastメソッドを2回使ってみたコード。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    deque.addLast("D");//addLastメソッドを実行
    deque.add("E");
    deque.add("F");
    deque.addLast("G");//addLastメソッドを実行
    System.out.println(deque);
}

【実行結果】
[A, B, C, D, E, F, G]
popメソッド

次は、リストの先頭の要素を取り出すpopメソッドを試してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    System.out.println("pop実行前: " + deque);

    String str = deque.pop();//popメソッドを実行
    System.out.println("pop実行後: " + deque);
    System.out.println("取り出したstrの中身: " + str);
}

【実行結果】
pop実行前: [A, B, C]
pop実行後: [B, C]
取り出したstrの中身: A

popメソッド実行前後の出力結果を見てみると、popメソッド実行時にdequeの先頭の要素が削除されていることが分かる。
つまり、popメソッドを用いてリストの先頭の要素を取り出すと、その要素はリストから削除される。

removeメソッド

次は、removeメソッドを試してみる。
removeメソッドpopメソッドと同様に、リストの先頭の要素を取り出すメソッドである。
以下のとおり、popの部分をremoveに書き換えて実行してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    System.out.println("remove実行前: " + deque);

    String str = deque.remove();//removeメソッドを実行。
    System.out.println("remove実行後: " + deque);
    System.out.println("取り出したstrの中身: " + str);
}

【実行結果】
remove実行前: [A, B, C]
remove実行後: [B, C]
取り出したstrの中身: A

popメソッドと同様の結果になった。
つまり、リストの先頭の要素を取り出したタイミングでリストから削除された。

removeFirstメソッド

次は、removeFirstメソッドを試してみる。
removeFirstメソッドpopメソッドremoveメソッドと同様に、リストの先頭の要素を取り出すメソッドである。
以下のとおり、popの部分をremoveFirstに書き換えて実行してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    System.out.println("removeFirst実行前: " + deque);

    String str = deque.removeFirst();//removeFirstメソッドを実行
    System.out.println("removeFirst実行後: " + deque);
    System.out.println("取り出したstrの中身: " + str);
}

【実行結果】
removeFirst実行前: [A, B, C]
removeFirst実行後: [B, C]
取り出したstrの中身: A

popメソッドremoveメソッドと同様の結果になった。
つまり、リストの先頭の要素を取り出したタイミングでリストから削除された。

removeLastメソッド

次は、リストの末端の要素を取り出すremoveLastメソッドを試してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    System.out.println("removeLast実行前: " + deque);

    String str = deque.removeLast();//removeLastメソッドを実行
    System.out.println("removeLast実行後: " + deque);
    System.out.println("取り出したstrの中身: " + str);
}

【実行前】
removeLast実行前: [A, B, C]
removeLast実行後: [A, B]
取り出したstrの中身: C

リストの末端の要素であるCが取得と同時にリストから削除されていることが分かる。

getFirstメソッド

次は、リストの先頭の要素を取り出すgetFirstメソッドを試してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    System.out.println("getFirst実行前: " + deque);

    String str = deque.getFirst();//getFirstメソッドを実行
    System.out.println("getFirst実行後: " + deque);
    System.out.println("取り出したstrの中身: " + str);
}

【実行結果】
getFirst実行前: [A, B, C]
getFirst実行後: [A, B, C]
取り出したstrの中身: A

getFirstメソッド実行前後の出力結果を見てみると、getFirstメソッド実行時にdequeの先頭の要素が削除されていないことが分かる。
つまり、getFirstメソッドを用いてリストの先頭の要素を取り出しても、その要素はリストから削除されない。

elementメソッド

次は、リストの先頭の要素を取り出すelementメソッドを試してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    System.out.println("element実行前: " + deque);

    String str = deque.element();//elementメソッドを実行
    System.out.println("element実行後: " + deque);
    System.out.println("取り出したstrの中身: " + str);
}

【実行結果】
element実行前: [A, B, C]
element実行後: [A, B, C]
取り出したstrの中身: A

elementメソッド実行前後の出力結果を見てみると、elementメソッド実行時にdequeの先頭の要素が削除されていないことが分かる。
つまり、elementメソッドを用いてリストの先頭の要素を取り出しても、その要素はリストから削除されない。

getLastメソッド

次は、リストの末端の要素を取り出すgetLastメソッドを試してみる。

public static void main(String args[]){
    Deque<String> deque = new ArrayDeque<String>();
    deque.add("A");
    deque.add("B");
    deque.add("C");
    System.out.println("getLast実行前: " + deque);

    String str = deque.getLast();//getLastメソッドを実行
    System.out.println("getLast実行後: " + deque);
    System.out.println("取り出したstrの中身: " + str);
}

【実行結果】
getLast実行前: [A, B, C]
getLast実行後: [A, B, C]
取り出したstrの中身: C

getLastメソッド実行前後の出力結果を見てみると、getLastメソッド実行時にdequeの末端の要素が削除されていないことが分かる。
つまり、getLastメソッドを用いてリストの末端の要素を取り出しても、その要素はリストから削除されない。

【Java】インターフェースの多重継承による菱形継承(ダイヤモンド継承)問題を理解する

ダイヤモンド継承でコンパイルエラーになる例

同じシグニチャを持つ複数のインターフェースをimplementsした場合、コンパイルエラーになる。

public interface ifParent {
    default void X(){
        System.out.println("interfaseParent");
    }
}
public interface ifA extends ifParent{
    @Override
    default void X(){
        System.out.println("interfaseA");
    }
}
public interface ifB extends ifParent{
    @Override
    default void X(){
        System.out.println("interfaseB");
    }
}
public class classC implements ifA ,ifB {   //ここでコンパイルエラーになる。

    public static void main(String[] args) {
        classC c = new classC();
        c.X();
    }
}

上記のソースをクラス図で表現すると以下のとおり。
f:id:shiakisudev:20181104191414j:plain classCがifAとifBをimplementsしたタイミングでコンパイルエラーになる。
これはclassCから見た場合、ifAとifBが同階層にあり、両者ともdefaultメソッドをOverrideしているため。
これを防ぐには以下のようにclassCでXメソッドをOverrideする必要がある。

public class classC implements ifA ,ifB {

    public static void main(String[] args) {
        classC c = new classC();
        c.X();  //classC
    }

    @Override
    public void X(){
        System.out.println("classC");
    }
}

ダイヤモンド継承でコンパイルエラーにならない例

複数のインターフェースが同階層にあってもメソッドが異なる階層(距離が離れている場合)近いほうのメソッドが呼ばれるため、コンパイルエラーにならない。

public interface ifParent {
    default void X(){
        System.out.println("interfaseParent");
    }
}
public interface ifA extends ifParent{
    @Override
    default void X(){
        System.out.println("interfaseA");
    }
}
public interface ifB extends ifParent{
}
public class classC implements ifA ,ifB {  

    public static void main(String[] args) {
        classC c = new classC();
        c.X();  //interfaseA
    }
}

上記ソースをクラス図で表現すると以下のとおり。
f:id:shiakisudev:20181104191431j:plain ダイヤモンド継承でコンパイルエラーになる例との違いはifBでOverrideしていない点。
より近い方でOverrideしたifAのXメソッドが呼ばれる。

【Java】匿名クラス(AnonymousClass)の書き方

匿名クラス(AnonymousClass)とは

匿名クラス(AnonymousClass)とはクラス名を持たないクラスのことを指し、
newキーワードともに「クラスの宣言」と「インスタンスの生成」を同時に行うことが出来る。
一般的にはインターフェースの実装をその場で行うために使用される。

構文

new InterFaceName(){
  //メソッド実装
}

インターフェースを匿名クラスとして実装した例

@FunctionalInterface
public interface IF {
    public void method1(String str);
}

匿名クラスとしてインターフェースを実装した例

public class Main {
    public static void main(String args[]){

        IF IF = new IF(){
            public void method1(String str){
                System.out.println(str);
            }
        };
        IF.method1("test");  //test
    }
}

上記を簡略化した実装例

public class Main {
    public static void main(String args[]){

        new IF(){
            public void method1(String str){
                System.out.println(str);
            }
        }.method1("test");
    }
                   
}

インターフェースを引数に取るメソッド

public class Main {
    public static void main(String args[]){
        Main m = new Main();
        m.callIF(new IF(){
            public void method1(String str){
                System.out.println(str);
            }
        });
    }
    public void callIF(IF IF){
        System.out.println("1");
        IF.method1("test");
        System.out.println("3");
    }
}

【Java】入れ子クラス(NestedClass)及びInnerClassの書き方

入れ子クラス(NestedClass)とは

あるブロック内で宣言されたクラスの総称を入れ子クラス(NestedClass)と云う。
入れ子クラス(NestedClass)には以下の種類がある。

クラス 説明
staticメンバークラス クラス内で宣言されるstaticなクラス
メンバークラス クラス内で宣言されるインスタンスクラス
ローカルクラス メソッド内で宣言されるクラス

また、上記の内、下2つを内部クラス(InnerClass)と云う。
これらのクラスはクラス名を持たずに利用することができ、このようなクラスを匿名クラスという。

public class Outer {
    private static class staticClass {}      //staticメンバークラス
    private class instanceClass {}            //メンバークラス(内部クラス)

    void methodA(){
      class localClass{}                   //ローカルクラス(内部クラス)
    }
}

メンバークラスとして宣言された入れ子クラスは通常のフィールドやメソッドのようにアクセス修飾子を付けることができる。
(ローカルクラスにアクセス修飾子を付けるとコンパイルエラーになる。)

内部クラス(メンバークラスの)メソッドを呼び出す

内部クラスのインスタンスを生成し、メソッドへアクセスする。

public class oya {

    public void callInner(){
        Inner i = new Inner();
        i.print();
    }

    public class Inner{
        public void print(){
            System.out.println("this code is a InnerClass");
        }
    }
}

呼び出し側

public class Main {

    public static void main(String[] args) {
        oya o = new oya();
        o.callInner();  //"this code is a InnerClass"
    }
}

staticメソッドが内部クラスを呼び出す方法

staticメソッドから内部クラスを利用するには、最初に外側のクラスのインスタンスを生成し、その参照を用いて内部クラスのインスタンスを生成してアクセスする。

public class oya {

    public static void callInnerByStatic(){
         new oya().new Inner().print();
    }
    public class Inner{
        public void print(){
            System.out.println("this code is a InnerClass");
        }
    }
}

呼び出し側

public class Main {

    public static void main(String[] args) {
        oya.callInnerByStatic();  //"this code is a InnerClass"
    }
}

【Java】enum(列挙型)の書き方

enum(列挙型)とは

複数の定数を1つにまとめてグループ化することができる型のこと。
Javaenum(列挙型)はフィールドやメソッドを宣言することが可能。
要点をまとめると以下のとおり。

  • 列挙定数は、デフォルトで0からの序数が割り当てられる。(配列等で云うIndexみたいなもの)
  • 列挙定数のみ宣言する場合は、最後の列挙定数の末尾にセミコロンは付けても付けなくても良い。
  • 但し、列挙定数の他にフィールドやメソッドも宣言する場合は、最後の列挙定数の末尾にセミコロンが必須。
  • フィールドやメソッドも宣言する場合は、列挙定数はそれらよりも手前に宣言しなければならない。
  • 列挙定数は暗黙的にpublic static finalなメンバーとなるため、修飾子を付けることはできない。
  • 列挙型自体はアクセス修飾子のみ付けることが可能。
  • 列挙型は暗黙的にjava.lang.Enumクラスを継承しているためクラスを継承(extends)することができない。
    但し、インターフェースは実装(implements)することが可能。
  • 列挙型はprivate型のコンストラクタしか宣言できない。それ以外の修飾子はコンパイルエラーになる。

enum(列挙型)の書き方

【構文】
修飾子 enum 型名{ 列挙定数1,列挙定数名2,列挙定数名3;}
★列挙定数のみ宣言した例

public enum enumColor {
    RED,BLUE,GREEN;  //最後の定数の後ろにセミコロンを付けた例。
}
public enum enumColor {
    DOG,CAT,COW      //最後の定数の後ろにセミコロンを付けない例。
}

★列挙定数の他にフィールドおよびメソッドを宣言した例

public enum enumColor {
    RED,BLUE,GREEN,YELLOW,PINK;

    private int val = 100;
    private int num;

    void x(){
        
    }
}

★列挙定数よりも先にフィールドやメソッドを宣言した例(コンパイルエラー)

public enum enumColor {           //ここでコンパイルエラー。

    private int val = 100;
    private int num;

    void x(){

    }
    RED,BLUE,GREEN,YELLOW,PINK;
}

列挙定数名を取得する方法

1.nameメソッドを使用する。

スーパークラスであるEnumクラスが、列挙定数名を文字列として取得するために提供しているnameメソッドを使用する方法。

public class test {

    public static void main(String[] args) {
        String color = enumColor.RED.name();
        System.out.println(color);            //REDが返る。
    }
}

2.toStringメソッドを使用する。

同じくスーパークラスであるEnumクラスがオーバーライドしているtoStringメソッドを使用する方法。

public class test {

    public static void main(String[] args) {
        String color = enumColor.RED.toString();
        System.out.println(color);            //REDが返る。
    }
}

文字列から列挙型インスタンスを取得する方法

文字列から列挙型インスタンスを取得するにはvalueOfメソッドを使用します。
この動きはコレクションでcontainsメソッドを使うのに似ています。

public class test {

    public static void main(String[] args) {
        enumColor color = enumColor.valueOf("RED");
        System.out.println(color);            //REDが返る。
    }
}

存在しない文字列を指定した場合は、コンパイルエラーにならず実行時にIllegalArgumentExceptionが発生します。

public class test {

    public static void main(String[] args) {
        enumColor color = enumColor.valueOf("BLACK"); //これは例外。
        System.out.println(color);
    }
}


Exception in thread "main" java.lang.IllegalArgumentException: No enum constant enumColor.BLACK
    at java.lang.Enum.valueOf(Enum.java:238)
    at enumColor.valueOf(enumColor.java:1)
    at test.main(test.java:4)

引数を2つ取るvalueOfメソッド

実はvalueOfメソッドには引数と2つ取るものがあります。
第1引数に指定したenum型の中に第2引数で指定した列挙定数があればそれを返します。
無ければ実行時にIllegalArgumentExceptionが発生します。

public class test {

    public static void main(String[] args) {
        enumColor color = java.lang.Enum.valueOf(enumColor.class, "YELLOW");
        System.out.println(color.toString());     //YELLOWが返る。
    }
}

列挙型の序数を取得する方法

同じくスーパークラスであるEnumクラスが持つordinalメソッドを使用します。

public class test {

    public static void main(String[] args) {
        int i = enumColor.PINK.ordinal();
        System.out.println(i);               //4が返る。
    }
}

列挙型の配列を取得する方法

同じくスーパークラスであるEnumクラスが持つvaluesメソッドを使用します。

public class test {

    public static void main(String[] args) {
        for(enumColor color : enumColor.values()){
            System.out.println(color.toString()+": "+ color.ordinal());
        }
    }
}

【実行結果】
RED: 0
BLUE: 1
GREEN: 2
YELLOW: 3
PINK: 4

【VSCode】便利なショートカットキーと拡張機能

VSCode独自のショートカットキーと拡張機能についてのまとめ。
Ctrl+fで検索みたいなどのアプリでも共通で有名なショートカットは省きます。

基本操作

コマンド 説明
Ctrl + h 置換。
Ctrl + , 設定画面を開く。
Ctrl + Shift + Enter 空行を追加する。
Atrl + Shift + ↑ カーソル行の内容を上の行にコピーする。
Atrl + Shift + ↓ カーソル行の内容を下の行にコピーする。
Ctrl + Shift + k カーソル行を削除する。
Ctrl + Shift + d デバッグを開く。
Ctrl + Shift + p コマンドパレットを開く。
Ctrl + Shift + x 拡張機能を開く。

拡張機能

Markdown All in One

VSCodemarkdown書きたいならコレ。
marketplace.visualstudio.com

ショートカット 説明
Ctrl + Shift + v プレビューを表示する。
コマンド 説明
Markdown:Print current document to HTML HTMLを生成する。

Markdown TOC

markdownで書いた文書の目次を生成するならコレ。 marketplace.visualstudio.com

https://raw.githubusercontent.com/AlanWalk/Markdown-TOC/master/img/insert-toc.gif

コマンド 説明
Markdown TOC:Insert/Update 目次を生成(更新)する。
Markdown TOC:Delete 目次を削除する。