TL;DR
- ã©ã ããªãã¸ã§ã¯ãã®åãã©ã¡ã¼ã¿ãåå¾ããã¹ãã¼ããªæ¹æ³ã¯ä»ã®æè¦ã¤ãã£ã¦ããªã
- ããåºç¤ããã°ã©ã ã§ãããããã¨ããããå ´åã¯ãã©ã ããç¦æ¢ãã¦ãå¿åã¯ã©ã¹ã使ã
- ããæ¹æ³ããã£ããæãã¦ãã ãã
æ¬æ
Javaã§åºç¤ããã°ã©ã çãªã®ãä½ãã¨ããã¸ã§ããªã¯ã¹ã®åãã©ã¡ã¼ã¿ãåå¾ããããã¨ãããã¾ããæ®éã®ã¯ã©ã¹ãå¿åã¯ã©ã¹ã®å ´åã¯ä»¥ä¸ã®ãããªãªãã¬ã¯ã·ã§ã³ã®ã³ã¼ãã§åå¾ãããã¨ãã§ãã¾ãã
import java.lang.reflect.ParameterizedType; import java.util.function.Consumer; public class FooTest { public static void main(String[] args) { System.out.println(getGenericTypeParam(new Foo())); System.out.println(getGenericTypeParam(new Bar())); System.out.println(getGenericTypeParam(Baz)); } private static Class<?> getGenericTypeParam(Consumer consumer) { ParameterizedType type = (ParameterizedType) consumer.getClass().getGenericInterfaces()[0]; return (Class) type.getActualTypeArguments()[0]; } private static class Foo implements Consumer<String> { @Override public void accept(String s) {} } private static class Bar implements Consumer<Integer> { @Override public void accept(Integer s) {} } private static Consumer<Void> Baz = new Consumer<Void>() { @Override public void accept(Void aVoid) { } }; }
å®è¡çµæ
class java.lang.String class java.lang.Integer class java.lang.Void
ãããããããã©ã ãã«ãªãã¨ãgetGenericInterfaces
ã¡ã½ããã®çµæã ParameterizedType
ã§ã¯ãªãåãªã java.lang.Object
ã®ã¯ã©ã¹åã«ãªããClassCastExceptionãçºçãã¦ãã¾ãã¾ãã
getGenericTypeParam((Consumer<Byte>) (b -> {}));
çµæ
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
ã¤ã¾ããã©ã ãå¼ã§çæãããé¢æ°ãªãã¸ã§ã¯ãããã¯åãã©ã¡ã¼ã¿ã®æ å ±ãæ¶ãã¦ããã®ã§ãããããã©ãã«ããã¦åå¾ãããã¨ãããã模索ãã¦ããã®ã§ãããçµå±ãã¡ã§ãããä¸çªæããã£ãã®ã¯ãã¡ãã®Gistã®ããæ¹ã§ãã
ã©ã ãå¼ãå®ç¾©ããã¦ããã¯ã©ã¹ã§ getDeclaredMethods
ã使ã£ã¦ã¡ã½ããä¸è¦§ãè¦ãã¨ããã®ã¯ã©ã¹å
ã§å®ç¾©ãããã©ã ãå¼ã«å¯¾å¿ããSyntheticã¡ã½ãããçæããã¦ãã¾ãã
import java.lang.reflect.Method; public class FooTest { public static void main(String[] args) { Runnable task1 = () -> task2(); System.out.println(task1.getClass().getName()); task1.run(); System.out.println(); for (Method method : FooTest.class.getDeclaredMethods()) { System.out.println(method); } } private static void task2() { Runnable task2 = () -> {}; System.out.println(task2.getClass().getName()); } }
ä¸è¨ã®ããã°ã©ã ãå®è¡ããã¨ä»¥ä¸ã®ãããªçµæãåºåããã¾ãã
FooTest$$Lambda$1/664223387 FooTest$$Lambda$2/666641942 public static void FooTest.main(java.lang.String[]) private static void FooTest.task2() private static void FooTest.lambda$task2$1() private static void FooTest.lambda$main$0()
ã©ã ãå¼ã§çæããããªãã¸ã§ã¯ãã¨Syntheticã¡ã½ããã«ã¯ã©ã¡ããååã« $1
çãªã¤ã³ããã¯ã¹ãã¤ãã¦ãããããããã®ã¤ã³ããã¯ã¹ã1対1ã§å¯¾å¿ãã¦ãããã§ããããã§ãå
ã®Gistã«ç¿ã£ã¦ä»¥ä¸ã® getGenericTypeParamSmart
ã追å ãã¾ãã
private static Class<?> getGenericTypeParamSmart(Consumer consumer) { String functionClassName = consumer.getClass().getName(); int lambdaMarkerIndex = functionClassName.indexOf("$$Lambda$"); if (lambdaMarkerIndex == -1) { // Not a lambda return getGenericTypeParam(consumer); } String declaringClassName = functionClassName.substring(0, lambdaMarkerIndex); int lambdaIndex = Integer.parseInt(functionClassName.substring(lambdaMarkerIndex + 9, functionClassName.lastIndexOf('/'))); Class<?> declaringClass; try { declaringClass = Class.forName(declaringClassName); } catch (ClassNotFoundException e) { throw new IllegalStateException("Unable to find lambda's parent class " + declaringClassName); } for (Method method : declaringClass.getDeclaredMethods()) { if (method.isSynthetic() && method.getName().startsWith("lambda$") && method.getName().endsWith("$" + (lambdaIndex - 1)) && Modifier.isStatic(method.getModifiers())) { return method.getParameterTypes()[0]; } } throw new IllegalStateException("Unable to find lambda's implementation method"); }
ãã®ä¸ã§ã以ä¸ã®ã³ã¼ããå®è¡ããã¨ã¡ããã¨åãã©ã¡ã¼ã¿ãåãã¦ããã§ã
public static void main(String[] args) { System.out.println(getGenericTypeParamSmart(new Foo())); System.out.println(getGenericTypeParamSmart(new Bar())); System.out.println(getGenericTypeParamSmart(Baz)); System.out.println(getGenericTypeParamSmart((Consumer<Byte>) (b -> {}))); System.out.println(getGenericTypeParamSmart((Consumer<Long>) (l -> {}))); }
class java.lang.String class java.lang.Integer class java.lang.Void class java.lang.Byte class java.lang.Long
ãã ããGistã«ãã³ã¡ã³ããã¾ããããã©ã ãå ã§ã©ã ããçæããå ´åãä¾ãã°ã以ä¸ã®ãã¿ã¼ã³ã§ã¯å¤±æãã¾ãã
public static void main(String[] args) { Runnable task = () -> { System.out.println(getGenericTypeParamSmart(new Foo())); System.out.println(getGenericTypeParamSmart(new Bar())); System.out.println(getGenericTypeParamSmart(Baz)); System.out.println(getGenericTypeParamSmart((Consumer<Byte>) (b -> {}))); System.out.println(getGenericTypeParamSmart((Consumer<Long>) (b -> {}))); }; task.run(); }
class java.lang.String class java.lang.Integer class java.lang.Void class java.lang.Long Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
ãªãã§ããããï¼ ã©ã ããªãã¸ã§ã¯ãã¨FooTestã¯ã©ã¹ã«å®ç¾©ããã¦ããSyntheticã¡ã½ãããæ¯è¼ãã¦ã¿ã¾ããã
public static void main(String[] args) { Runnable task = () -> { Consumer<Byte> byteConsumer = b -> {}; Consumer<Long> longConsumer = l -> {}; System.out.println("byteConsumer: " + byteConsumer.getClass().getName()); System.out.println("longConsumer: " + longConsumer.getClass().getName()); }; System.out.println("task: " + task.getClass().getName()); task.run(); System.out.println(); for (Method method : FooTest.class.getDeclaredMethods()) { if (method.isSynthetic()) { System.out.println(method.toString()); } } }
ããã®å®è¡çµæã¯ä»¥ä¸ã®ããã«ãªãã¾ã
task: FooTest$$Lambda$1/664223387 byteConsumer: FooTest$$Lambda$2/1349393271 longConsumer: FooTest$$Lambda$3/159413332 private static void FooTest.lambda$main$2() private static void FooTest.lambda$null$1(java.lang.Long) private static void FooTest.lambda$null$0(java.lang.Byte)
ã¤ã¾ã:
task
ãªãã¸ã§ã¯ãFooTest$$Lambda$1
ã«å¯¾å¿ããSyntheticã¡ã½ããã¯lambda$main$2
byteConsumer
ãªãã¸ã§ã¯ãFooTest$$Lambda$2
ã«å¯¾å¿ããSyntheticã¡ã½ããã¯lambda$null$0
longConsumer
ãªãã¸ã§ã¯ãFooTest$$Lambda$3
ã«å¯¾å¿ããSyntheticã¡ã½ããã¯lambda$null$1
ã«ããªãããã§ããçªå·ã®å¯¾å¿ãããã¦ãã®ã§ãééã£ãã¡ã½ãããæ¤ç´¢ãã¦ãã¾ã£ã¦ããããã§ãããããã訳ã§ããã®æ¹æ³ã¯ä½¿ãã¾ããã§ããã
ãã®ãã¨ãããã調ã¹ã¦ã¿ã¾ããããåãã©ã¡ã¼ã¿ãã¡ããã¨åå¾ããæ¹æ³ã¯è¦ã¤ããã¾ããã§ãããã¨ããããã§ãããããããã°ã©ã ãæ¸ãããã¨ãã¯ä»ã®æã¯ã©ã ããç¦æ¢ããã»ããè¯ãããã§ãã
private static Class<?> getGenericTypeParam(Consumer consumer) { String functionClassName = consumer.getClass().getName(); if (functionClassName.contains("$$Lambda$")) { throw new UnsupportedOperationException("Lambda is not supported"); } ParameterizedType type = (ParameterizedType) consumer.getClass().getGenericInterfaces()[0]; return (Class) type.getActualTypeArguments()[0]; }
æçµçãªã³ã¼ãã¯ä»¥ä¸ã®ãããªæãã«ãªãã¾ãã
import java.lang.reflect.ParameterizedType; import java.util.function.Consumer; public class FooTest { public static void main(String[] args) { System.out.println(getGenericTypeParam(new Foo())); System.out.println(getGenericTypeParam(new Bar())); System.out.println(getGenericTypeParam(Baz)); try { System.out.println(getGenericTypeParam((Consumer<Byte>) (b -> {}))); } catch (UnsupportedOperationException e) {} } private static Class<?> getGenericTypeParam(Consumer consumer) { String functionClassName = consumer.getClass().getName(); if (functionClassName.contains("$$Lambda$")) { throw new UnsupportedOperationException("Lambda is not supported"); } ParameterizedType type = (ParameterizedType) consumer.getClass().getGenericInterfaces()[0]; return (Class) type.getActualTypeArguments()[0]; } private static class Foo implements Consumer<String> { @Override public void accept(String s) {} } private static class Bar implements Consumer<Integer> { @Override public void accept(Integer s) {} } private static Consumer<Void> Baz = new Consumer<Void>() { @Override public void accept(Void aVoid) {} }; }