author-pic

Ferry S

An ISTJ, Type 5, Engineer, Gamer, and Thriller-Movies-Lover
Trik Checked Exception di Java Lambda
Mon. Nov 9th, 2020 02:11 PM2 mins read
Trik Checked Exception di Java Lambda
Source: Bing Image Creator - Lambda

Pada Java 8 terdapat Functional Interface yang dapat digunakan sebagai Lambda. Namun mayoritas Functional Interface tersebut by default tidak melakukan throws Exception. Ini cukup ribet kalau kita memanggil method yang throws checked exception pada scope Lambda pada umumnya. Contohnya kita ingin melakukan looping String yang berisi path sebuah text menggunakan lambda kemudian text tersebut dibaca dan di-print pada console seperti pada code berikut:

public class TextPrinter{
	public void printText(String path)throws Exception{
		try(FileInputStream fis = new FileInputStream(path)){
			int intChar;
			while((intChar = fis.read()) != -1){
				System.out.print((char) intChar);
			}
		}
	}

	public void execute(List<String> paths) throws Exception{
		paths.forEach((path) -> printText(path));
	}
}

Code di atas akan compile error karena method foreach() menerima Functional Interface Consumer yang tidak melakukan throws checked exception, sedangkan method printText() yang dipanggil dalam lambda throws Exception. Mau gak mau code pada lambda harus ditambahkan try catch dulu di dalamnya. Contohnya seperti berikut:

public class TextPrinter{
	public void printText(String path)throws Exception{
		try(FileInputStream fis = new FileInputStream(path)){
			int intChar;
			while((intChar = fis.read()) != -1){
				System.out.print((char) intChar);
			}
		}
	}

	public void execute(List<String> paths) throws Exception{
		paths.forEach((path) -> {
			try{
				printText(path);
			} catch(Exception e){
				throw new RuntimeException(e.getMessage(), e);
			}
		});
	}
}

Kalau gw sih biasanya kalau ketemu kasus seperti ini akan melakukan try catch dan di dalam scope catch-nya melakukan throws RuntimeException lagi dengan membungkus ulang exception sebelumnya.

Masalah pada solusi sebelumnya adalah code-nya agak verbose, harus melakukan try catch dan throws lagi di setiap code seperti kasus di atas. Solusi lainnya adalah membuat sebuah functional interface lainnya yang support checked exception lalu code try catch tersebut dibungkus ke dalam utilities. Contohnya seperti ini:

public interface Mute{
	void run() throws Throwable;

	static void run(Mute mute){
		try{
			mute.run();
		} catch(Throwable e){
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	static <V> V run(Callable<V> mute){
		try{
			return mute.call();
		} catch(Throwable e){
			throw new RuntimeException(e.getMessage(), e);
		}
	}

}

Pada interface di atas kita punya dua utilities, yang satu untuk lambda yang tanpa nilai return, yang satunya untuk lambda yang mengembalikan nilai. Untuk lambda yang mengembalikan nilai kita memanfaat interface Callable yang memang support checked exception. Dengan utilities di atas, code TextPrinter jadi seperti ini:

public class TextPrinter{
	public void printText(String path){
		try(FileInputStream fis = new FileInputStream(path)){
			int intChar;
			while((intChar = fis.read()) != -1){
				System.out.print((char) intChar);
			}
		}
	}

	public void execute(List<String> paths) throws Exception{
		paths.forEach((path) ->
				Mute.run(() -> printText(path)));
	}

	public String getText(String path)throws Exception{
		FileInputStream fis = new FileInputStream(path);
		int intChar;
		StringBuilder builder = new StringBuilder();
		while(( intChar = fis.read() ) != -1){
			builder.append((char) intChar);
		}
		fis.close();
		return builder.toString();
	}

	public List<String> getTexts(List<String> paths) throws Exception{
		return paths.stream().map((path) ->
				Mute.run(() ->  getText(path)))
				.collect(Collectors.toList());
	}

Nah, akhirnya code-nya jadi simple dan lebih enak dibaca, IMHO.

Cara lainnya adalah dengan dengan menggunakan library Project Lombok.

@SneakyThrows
public void printText(String path){
	try(FileInputStream fis = new FileInputStream(path)){
		int intChar;
		while((intChar = fis.read()) != -1){
			System.out.print((char) intChar);
		}
	}
}

public void execute(List<String> paths) throws Exception{
	paths.forEach((path) -> printText(path)));
}

Cukup tambahkan @SneakyThrows pada method yang melakukan throws checked exception agar exception tersebut dihandle oleh lombok saat compile.