Home > Archives > Javaメソッド参照

Javaメソッド参照

Publish:

メソッド参照は、JDK1.8で導入された構文。 関数型インターフェース(抽象メソッドが1つだけ定義されているインターフェース)の変数にメソッドそのものを代入することが出来る。
※メソッド参照の形式は常に以下の2種となる。
①クラス名::メソッド名(staticメソッド)
②インスタンス変数名::メソッド名(インスタンスメソッド)

インスタンスメソッド


インスタンス変数名::メソッド名

public class Object1 {
	public String contact(String a, String b) {
		return a + b;
	}
	public static double getRandom() {
		return Math.random();
	}
	public static void getText(String prex) {
		LocalDate date1 = LocalDate.now();
		System.out.println(prex + date1.toString());
	}
}
/***** 引数がなし ****/
String str = "test";
IntSupplier supplier = str::length;// 参照メソッド
System.out.println(supplier.getAsInt());

IntSupplier supplier1 = () -> str.length();
System.out.println(supplier1.getAsInt());// ラムダ式

IntSupplier supplier2 = new IntSupplier() {// 匿名クラス
	@Override
	public int getAsInt() {
		return str.length();
	}
};
System.out.println(supplier2.getAsInt());

/***** 引数が1個 ****/
Consumer<String> c = System.out::println;// 参照メソッド
c.accept("テスト1");

Consumer<String> c2 = (String s) -> System.out.println(s);// ラムダ式
c2.accept("テスト2");

Consumer<String> c3 = new Consumer<String>() {// 匿名クラス
	@Override
	public void accept(String s) {
		System.out.println(s);
	}
};
c3.accept("テスト3");

/***** 引数が2個 ****/
Object1 obj1 = new Object1();
BinaryOperator<String> b = obj1::contact;// 参照メソッド
System.out.println(b.apply("abc", "123"));

BinaryOperator<String> b2 = (str1, str2) -> {
	return str1 + str2;
};// ラムダ式
System.out.println(b2.apply("abc", "123"));

BinaryOperator<String> b3 = new BinaryOperator<String>() {// 匿名クラス
	@Override
	public String apply(String t, String u) {
		return t + u;
	}
};
System.out.println(b3.apply("abc", "123"));

staticメソッド


クラス名::メソッド名

public class Object1 {
	public String contact(String a, String b) {
		return a + b;
	}
	public static double getRandom() {
		return Math.random();
	}
	public static void getText(String prex) {
		LocalDate date1 = LocalDate.now();
		System.out.println(prex + date1.toString());
	}
}
/***** 引数がなし ****/

DoubleSupplier supplier = Object1::getRandom;// 参照メソッド
System.out.println(supplier.getAsDouble());

DoubleSupplier supplier1 = () -> Object1.getRandom();
System.out.println(supplier1.getAsDouble());// ラムダ式

DoubleSupplier supplier2 = new DoubleSupplier() {// 匿名クラス
	@Override
	public double getAsDouble() {
		return Math.random();
	}
};
System.out.println(supplier2.getAsDouble());

/***** 引数が1個 ****/
Consumer<String> c = Object1::getText;// 参照メソッド
c.accept("テスト1");

Consumer<String> c2 = (String s) -> {
	LocalDate date1 = LocalDate.now();
	System.out.println(s + date1.toString());
};// ラムダ式
c2.accept("テスト2");

Consumer<String> c3 = new Consumer<String>() {// 匿名クラス
	@Override
	public void accept(String s) {
		LocalDate date1 = LocalDate.now();
		System.out.println(s + date1.toString());
	}
};
c3.accept("テスト3");

/***** 引数が2個 ****/
IntBinaryOperator b = Integer::sum;// 参照メソッド
System.out.println(b.applyAsInt(100, 200));

IntBinaryOperator b2 = (str1, str2) -> {
	return str1 + str2;
};// ラムダ式
System.out.println(b2.applyAsInt(100, 200));

IntBinaryOperator b3 = new IntBinaryOperator() {// 匿名クラス
	@Override
	public int applyAsInt(int t, int u) {
		return t + u;
	}
};
System.out.println(b3.applyAsInt(100, 200));

親クラスとサブクラス


メソッド参照はラムダ式と同義なので、明示的に「クラス名::メソッド名」の形式で指定しても、対象自体のインスタンスのクラスのメソッドが呼ばれる

public static void checkNew() {
/**
 * メソッド参照はラムダ式と同義なので、明示的に「クラス名::メソッド名」の形式で指定しても、対象自体のインスタンスのクラスのメソッドが呼ばれる
 *
 */
List<Checker> list = Arrays.asList(new Checker(1), new Checker(2), new Checker(3), new SubChecker(3),
		new SubChecker(4), new SubChecker(5));
// ラムダ式で指定
System.out.println(list.stream().filter(checker -> checker.check()).collect(Collectors.toList()));

// メソッド参照で指定
// ×System.out.println(list.stream().filter(SubChecker::check).collect(Collectors.toList()));//コンパイルエラー

// ラムダ式で親クラスを指定といっても
System.out.println(list.stream().filter((Checker check) -> check.check()).collect(Collectors.toList()));

// メソッド参照で親クラスを指定といっても
System.out.println(list.stream().filter(Checker::check).collect(Collectors.toList()));

}

実行結果:↓

[com.whisper.methodref.Checker.2, com.whisper.methodref.SubChecker.5]
[com.whisper.methodref.Checker.2, com.whisper.methodref.SubChecker.5]
[com.whisper.methodref.Checker.2, com.whisper.methodref.SubChecker.5]

コンストラクター参照


クラス名::new

IntFunction<String[]> factory = String[]::new;//メソッド参照
String[] array = factory.apply(10);

IntFunction<String[]> factory1 = (int n) -> new String[n];//ラムダ式
String[] array1 = factory1.apply(10);

IntFunction<String[]> factory2 = new IntFunction<String[]>() {//匿名クラス
	@Override
	public String[] apply(int value) {
		return new String[value];
	}
};
String[] array3 = factory2.apply(10);

各要注意点


public class MethodReference4Special {
	public int method2() {
		return 123;
	}
	private static int method4() {
		return 1234;
	}
	public void method1() {// 自分自身のクラスにあるインスタンスメソッドを渡す場合は、「this::」を付ける
		IntSupplier supplier = this::method2;
		System.out.println(supplier.getAsInt());
	}

	public void method3() {// 自分自身のクラスにあるstaticメソッドを渡す場合は、「自クラス名::」を付ける
		IntSupplier supplier = MethodReference4Special::method4;
		System.out.println(supplier.getAsInt());
	}
}	
/**
 * 引数が2つ(A a, B b)ある関数を渡す場合、 「a.method(b)」という呼び出しになるなら、
 * 「A::method」という形でクラス名を用いたメソッド参照が渡せる。
 */
BiFunction<List<String>, String, Boolean> f = List<String>::add;
List<String> list = new ArrayList<>();
f.apply(list, "abc");
System.out.println(list);

// ラムダ式を使ってもメソッド参照を使っても同じ結果が出力されている。
List<String> list1 = new ArrayList<>();
BiFunction<List<String>, String, Boolean> f2 = (list2, s) -> list2.add(s);
f2.apply(list1, "abc");
System.out.println(list);

構文上はstaticメソッドでもインスタンスメソッドでも指定できるが、使用時に一意に決定できない場合(曖昧な場合)はコンパイルエラーになる。

public class Object2 {
	protected final int value;
	public Object2() {
		this.value =  0;
	}
	public Object2(int value) {
		this.value = value;
	}
	// インスタンスメソッドのisOk
	public boolean isOk() {
		return true;
	}
	// staticメソッドのisOk
	public static boolean isOk(Object2 obj) {
		return obj.value % 2 == 0;
	}
}
Object2 obj2 = new Object2();
IntPredicate p = value -> Object2.isOk(obj2);
System.out.println(p.test(5));

BooleanSupplier b = () -> obj2.isOk();
System.out.println(b.getAsBoolean());

//× Predicate<Object2> p2 = obj2::isOk;//The method isOk(Object2) from the type Object2 should be accessed in a static

//× Predicate<Object2> p2 = Object2::isOk;//Ambiguous method reference: both isOk() and isOk(Object2) from the type Object2 are eligible

List<Object2> list = IntStream.rangeClosed(1, 10).mapToObj(Object2::new).collect(Collectors.toList());

// ラムダ式によるインスタンスメソッド呼び出し
System.out.println(list.stream().filter(value -> value.isOk()).collect(Collectors.toList()));

// ラムダ式によるstaticメソッド呼び出し
System.out.println(list.stream().filter(value -> Object2.isOk(value)).collect(Collectors.toList()));

// メソッド参照→コンパイルエラー
//× System.out.println(list.stream().filter(Value::isOk).collect(Collectors.toList()));
public static <T> void methodG(T arg) {//ジェネリクス
	System.out.println(arg);

}
Consumer<String> c = MethodReference4Special::<String>methodG;// 引数型を明示的に指定したい場合、「::」の直後に「<型引数>」を指定する
c.accept("111");

Disclaimer: This article is based on BY-NC-SA license. Reproduced please specify switched from: whisper