晴耕雨読

working in the fields on fine days and reading books on rainy days

OOP in Java

Javaにおけるオブジェクト指向のまとめ

目次

クラスの定義

  • class というキーワードでクラスを宣言
  • new クラス名() でインスタンスの生成
class Foo {}

Foo foo = new Foo();

コンストラクタ、フィールド、メソッド

  • クラス名と同名の関数を宣言するとコンストラクタになります (コンストラクタの返り値は書かない)
  • フィールドとメソッドのアクセス修飾子を省略した場合は、 同一パッケージからアクセスできるメンバとなります
class Point {
    public int x;
    public int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public Point add(Point other) {
        return new Point(this.x + other.x, this.y + other.y);
    }

    public String toString() {
        return "(" + this.x + ", " + this.y + ")";
    }
}


public class Main {
    public static void main(String[] args) {
        Point p1 = new Point(1, 2);
        Point p2 = new Point(3, 4);
        System.out.println( p1.add(p2) );
    }
}

クラスメソッド、クラス変数

  • クラスメソッドは static 修飾子を使います
  • クラス変数も static 修飾子を使います
class User {
    // staticフィールド
    private static int userNum = 0;

    // staticメソッド
    public static int getUserNum() {
        return userNum;
    }

    public User() {
        userNum++;
    }
}


public class Main {
    public static void main(String[] args) {
        System.out.println(User.getUserNum()); //=> 0
        User user1 = new User();
        User user2 = new User();
        System.out.println(User.getUserNum()); //=> 2
    }
}

アクセス権

アクセス修飾子は public と private と protected の3種類あります。 アクセス修飾子を省略した場合は、同一パッケージからアクセスできるようになります。 なので、基本的には全てのメンバにアクセス修飾子をつけるのが、混乱が少なくて良いと思います。

オーバーロード

同じ名前のメソッドを定義することでオーバーロードすることができます。

class Sample {
    public static int foo(int x) {
        return x + 1;
    }
    public static String foo(String x) {
        return x + "1";
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println( Sample.foo(123) );   // => 124
        System.out.println( Sample.foo("123") ); // => 1231
    }
}

継承、オーバーライド

  • 継承をするには class 子クラス extends 親クラス と書きます
  • 親クラスへの参照は super を使います
  • オーバーライドをするには、@Override アノテーションをメソッド宣言の前に付けます
class Point {
    public int x;
    public int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public String toString() {
        return "(" + this.x + ", " + this.y + ")";
    }
}

class Point3D extends Point {
    public int z;

    public Point3D(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }

    @Override
    public String toString() {
        return "(" + this.x + ", " + this.y + ", " + this.z + ")";
    }
}


public class Main {
    public static void main(String[] args) {
        System.out.println( new Point(1, 2) );      // => "(1, 2)"
        System.out.println( new Point3D(1, 2, 3) ); // => "(1, 2, 3)"
    }
}

抽象クラス

抽象クラス・抽象メソッドを定義するには、abstract キーワードを付けます。

// 抽象クラスの定義
abstract class AbstractMusic {
    public abstract void play();
    public abstract void stop();
}

class Music extends AbstractMusic {
    @Override
    public void play() {
        System.out.println("play!");
    }

    @Override
    public void stop() {
        System.out.println("stop!");
    }
}


public class Main {
    public static void main(String[] args) {
        Music music = new Music();
        music.play(); // => "play!"
    }
}

インターフェース

インターフェースを定義するには、interface キーワードを付けます。

interface USBInterface {
    public static final float USB_VERSION = 3.0;
    public abstract boolean connectUSB();
    public abstract boolean disconnectUSB();
}

// Printerクラス は USBInterface を実装する
class Printer implements USBInterface {
    public boolean connectUSB() {
        // ...
    }

    public boolean disconnectUSB() {
        // ...
    }

    public boolean print(PDF pdf) {
        // ...
    }
}

また、Java8ではインターフェースに「デフォルトメソッド」を定義できるようになりました。

interface GreetingInterface {
    default String sayHello() {
        return "Hello Java8!";
    }
}

class GreetingInterfaceImpl implements GreetingInterface {}


class Main {
    public static void main(String[] args) {
        System.out.println(new GreetingInterfaceImpl().sayHello());
        // => Hello Java8!
    }
}

ジェネリックス

  • ジェネリックを定義するには、class クラス名<仮の型名> と書きます
  • 型を制限することもできます
    • <仮の型名 extends 他のクラス名> と書けば、他のクラスを派生しているクラスに限定することができます
    • <仮の型名 super 他のクラス名> と書けば、他のクラスが継承しているクラスに限定することができます
  • 型を制限するときは、クラス名だけでなく、インターフェース名で指定することも可能です
// Rangeクラスは、上界と下界を持つ範囲クラス
// T型はComparableインターフェースに限定
class Range<T extends Comparable<T>> {
    T begin;
    T end;

    public Range(T begin, T end) {
        if (begin.compareTo(end) > 0) {
            throw new RuntimeException("Range: warning: *begin* must be less than *end*");
        }
        this.begin = begin;
        this.end   = end;
    }

    public boolean include(T item) {
        return (item.compareTo(begin) >= 0 && item.compareTo(end) < 0);
    }
}


public class Main {
    public static void main(String[] args) {
        Range<Integer> intRange = new Range<Integer>(new Integer(1), new Integer(9));
        System.out.println(intRange.include(5)); // => true

        Range<String> stringRange = new Range<String>("a", "e");
        System.out.println(stringRange.include("c")); // => true
        System.out.println(stringRange.include("z")); // => false
    }
}