{"id":873,"date":"2015-08-04T08:25:34","date_gmt":"2015-08-04T07:25:34","guid":{"rendered":"http:\/\/benjiweber.co.uk\/blog\/?p=873"},"modified":"2015-08-16T21:27:07","modified_gmt":"2015-08-16T20:27:07","slug":"lambda-type-references","status":"publish","type":"post","link":"https:\/\/benjiweber.co.uk\/blog\/2015\/08\/04\/lambda-type-references\/","title":{"rendered":"Lambda Type References"},"content":{"rendered":"<p class=\"lead\">We&#8217;re used to type erasure ruining our day in Java. Want to create a new T() in a generic method? Normally not possible. We can pass a Class&lt;T&gt; around, but that doesn&#8217;t work for generic types. We can&#8217;t write List&lt;String&gt;.class <\/p>\n<p>One exception to this was using <a href=\"http:\/\/gafter.blogspot.co.uk\/2006\/12\/super-type-tokens.html\">super type tokens<\/a>. We can get the generic type of a super class at runtime, so if we can force the developer to subclass our type, then we&#8217;re able to find out our own generic types. <\/p>\n<p>It let us do things like <\/p>\n<pre lang=\"java\">\r\nList<String> l1 = new TypeReference<ArrayList<String>>() {}.newInstance();\r\n<\/pre>\n<p>This was really convenient prior to Java 8, because without lambdas we had to use anonymous inner classes to represent functions, and they could double up as type references.<\/p>\n<p>Unfortunately, the super type token does not work with Lambdas, because Lambdas are not just sugar for anonymous inner classes. They are implemented differently.<\/p>\n<p>However, there&#8217;s another trick we can use to get the generic type. It&#8217;s far more hacky implementation-wise, so probably not useful in a real scenario, but I think it&#8217;s neat nonetheless.<\/p>\n<p>Here&#8217;s an example of what we can do, a method that takes a TypeReference&lt;T&gt; and creates an instance of that type <\/p>\n<pre lang=\"java\">\r\npublic static <T> T create(TypeReference<T> type) {\r\n    return type.newInstance();\r\n}\r\n<\/pre>\n<p>So far just the same as the supertype tokens approach. However, to use it we just need to pass an identity lambda.<\/p>\n<p>This prints hello world<\/p>\n<pre lang=\"java\">\r\nArrayList<String> list = create(i->i);\r\nlist.add(\"hello\");\r\nlist.add(\"world\");\r\nlist.forEach(System.out::println);\r\n<\/pre>\n<p>This prints hello=1 world=2<\/p>\n<pre lang=\"java\">\r\nLinkedHashMap<String, Integer> map = create(i->i);\r\nmap.put(\"hello\", 1);\r\nmap.put(\"world\", 2);\r\nmap.entrySet().forEach(System.out::println);\r\n<\/pre>\n<p>We could also use as a variable. This prints String<\/p>\n<pre lang=\"Java\">\r\nTypeReference<String> ref = i->i;\r\nSystem.out.println(ref.type().getSimpleName()); \r\n<\/pre>\n<p>Unfortunately, it won&#8217;t work for the main motivation for supertype tokens &#8211; we can&#8217;t use this TypeReference as a key in a map because it will be a different instance each time.<\/p>\n<h2>First Attempt &#8211; Casting<\/h2>\n<p>For my first attempt I noticed that if we try casting something, and the cast is invalid we&#8217;ll get a ClassCastException at runtime that will tell us exactly what the type actually is. This does work with lambdas, since they&#8217;re translated into normal methods.<\/p>\n<p>Underneath the above Java snippet with a TypeReference&lt;String&gt; has been translated into something like<\/p>\n<pre>\r\nprivate static java.lang.String lambda$main$0(java.lang.String input) {\r\n    return input;\r\n}\r\n<\/pre>\n<p>As you can see, calling this with a type other than String is going to generate a ClassCastException.<\/p>\n<p>So we could invoke the lambda with a type other than it is expecting and pull the type name from the resulting exception.<\/p>\n<p><a href=\"https:\/\/github.com\/benjiman\/lambda-type-references\/blob\/730f123ee2bd2b8a1ffefb647fc0803a72259315\/src\/main\/java\/com\/benjiweber\/typeref\/Newable.java\">This worked<\/a>, but suggested that a better way is possible since we&#8217;re invoking  a real method, that we should be able to interrogate with reflection.<\/p>\n<h2>Better Attempt &#8211; Reflection<\/h2>\n<p>If we force our lambda to be Serializable, by having our TypeReference interface implement Serializable we can get access to a <a href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/api\/java\/lang\/invoke\/SerializedLambda.html\">SerializableLambda<\/a> instance. This gives us access to the containing class and the lambda name, and then we&#8217;re able to use the normal reflection API to interrogate the types.<\/p>\n<p><a href=\"https:\/\/github.com\/benjiman\/lambda-type-references\/blob\/master\/src\/main\/java\/com\/benjiweber\/typeref\/MethodFinder.java\">Here&#8217;s a MethodFinder interface<\/a> that we can extend our TypeReference from which gives us access to the Parameter types<\/p>\n<h2>Parameter Objects<\/h2>\n<p>Let&#8217;s consider a more concrete example of why lambdas that are aware of their types can be useful. One application is parameter objects. Extract parameter-object is a common refactoring.<\/p>\n<p>It lets us go from a method with too many parameters, to a method that takes a parameter object. <\/p>\n<p>e.g.<\/p>\n<pre lang=\"java\">\r\nList<Customer> customers = listCustomers(dateFrom, includeHidden, companyName, haveOrders);\r\n<\/pre>\n<p>becomes<\/p>\n<pre lang=\"java\">\r\nList<Customer> customers = listCustomers(customerQuerySpec);\r\n<\/pre>\n<p>Unfortunately the way this is commonly implemented means simply moves the problem one level up to the constructor of the parameter object.<\/p>\n<p>e.g. <\/p>\n<pre lang=\"java\">\r\nCustomerQuerySpecification customerQuerySpec = new CustomerQuerySpecification(dateFrom, includeHidden, companyName, haveOrders);\r\nList<Customer> customers = listCustomers(customerQuerySpec);\r\n<\/pre>\n<p>We still have just as many parameters, and still just as hard to follow. Furthermore, we now have to import the CustomerQuerySpecification type. The CustomerQuerySpecification type that your IDE might generate for you is also quite big. So this isn&#8217;t ideal.<\/p>\n<p>At this point we might reach for the <a href=\"http:\/\/benjiweber.co.uk\/blog\/2014\/11\/02\/builder-pattern-with-java-8-lambdas\/\">builder pattern, or a variation thereof<\/a>, to help us name our parameters. However, there are alternatives.<\/p>\n<p>If we were using JavaScript we might pass an object literal in this scenario, to allow ourselves to have default parameter values and named parameters.<\/p>\n<pre lang=\"javascript\">\r\nlistCustomers({\r\n    includeHidden = true,\r\n    companyName = \"A Company\"\r\n});\r\n<\/pre>\n<p>We can achieve something similar in Java using lambdas. (Or we could pass an anonymous inner class and use double-brace initialisation to override values)<\/p>\n<p>First of all we&#8217;ll create a Parameter Object to store our parameters. It can also have default values. Instead of using getters\/setters and a constructor I&#8217;m going to deliberately use public fields.<\/p>\n<pre lang=\"java\">\r\npublic static class CustomerQueryOptions {\r\n    public Date from = null;\r\n    public boolean includeHidden = true;\r\n    public String companyName = null;\r\n    public boolean haveOrders = true;\r\n}\r\n<\/pre>\n<p>Now we want a way of overriding these default values in a given call to our method. One way of doing this is instead of accepting the CustomerQueryOptions directly, accepting a function that mutates the CustomerQueryOptions. If we did this then we can easily specify our overrides at the callsite.<\/p>\n<pre lang=\"java\">\r\nlistCustomers(config -> {\r\n    config.includeHidden = true;\r\n    config.companyName = \"A Company\";\r\n});\r\n<\/pre>\n<p>You might notice that this lambda looks a lot like a Consumer&lt;CustomerQueryOptions&gt; &#8211; it accepts a config and returns nothing.<\/p>\n<p>We could just use a Consumer as is, but we can make life easier for ourselves with a little utility method that just gives us the config back and applies the function to it.<\/p>\n<p>Let&#8217;s make a Parameters interface that extends consumer. We&#8217;ll add a default method to it that returns our config. It instantiates the config for us, applies the consumer function to it in order to override any default values, and then returns the instantiated config.<\/p>\n<p>First we&#8217;ll need a way of creating a type of the CustomerQueryOptions ourselves, this is where our Newable&lt;T&gt; interface comes in. We define a NewableConsumer&lt;T&gt;<\/p>\n<pre lang=\"java\">\r\ninterface NewableConsumer<T> extends Consumer<T>, Newable<T> {\r\n    default Consumer<T> consumer() {\r\n        return this;\r\n    }\r\n}\r\n<\/pre>\n<p>And now we define our Parameters interface extending NewableConsumer<\/p>\n<pre lang=\"java\">\r\npublic interface Parameters<T> extends NewableConsumer<T> {\r\n    default T get() {\r\n        T t = newInstance(); \/\/ provided by Newable<T>\r\n        accept(t); \/\/ apply our config\r\n        return t; \/\/ return the config ready to use\r\n    }\r\n}\r\n<\/pre>\n<pre lang=\"java\">\r\npublic static class CustomerQueryOptions {\r\n    public Date from = null;\r\n    public boolean includeHidden = true;\r\n    public String companyName = null;\r\n    public boolean haveOrders = true;\r\n}\r\npublic List<Customer> listCustomers(Parameters<CustomerQueryOptions> spec) {\r\n\r\n    System.out.println(spec.get().haveOrders);\r\n\r\n    \/\/ ...\r\n}\r\n<\/pre>\n<p>This would even work with generic types. <\/p>\n<p>The following will print hello world.<\/p>\n<pre lang=\"java\">\r\nfoo(list -> {\r\n    list.add(\"Hello\");\r\n    list.add(\"World\");\r\n    \/\/ list.add(5); this would be a compile failure\r\n});\r\n    \r\npublic static void foo(Parameters<ArrayList<String>> params) {\r\n    params.get().forEach(System.out::println);\r\n}\r\n<\/pre>\n<h2>Summary<\/h2>\n<p>We can use a hack to make lambdas aware of their generic type. It&#8217;s a shame that it&#8217;s rather too terrible to use for real, because it would be really useful for some of the reasons outlined above. <\/p>\n<p>Unlike the super type tokens we also cannot use it as a key in a map because we&#8217;ll get a different lambda instance each time. <\/p>\n<p>Does anyone have an alternative approach?<\/p>\n<p>This post was inspired by <a href=\"https:\/\/twitter.com\/duncanmcg\">Duncan<\/a>&#8216;s use of this pattern in <a href=\"https:\/\/github.com\/dmcg\/jmock-play\/blob\/52ec90c6bc68aeea7b1b7e45d9094f363b827485\/src\/main\/java\/org\/jmock\/function\/internal\/ParameterTypeFinder.java\">Expec8ions<\/a><\/p>\n<p>The code from this post <a href=\"https:\/\/github.com\/benjiman\/lambda-type-references\">is available on github<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We&#8217;re used to type erasure ruining our day in Java. Want to create a new T() in a generic method? Normally not possible. We can pass a Class&lt;T&gt; around, but that doesn&#8217;t work for generic types. We can&#8217;t write List&lt;String&gt;.class One exception to this was using super type tokens. We can get the generic type&#8230;  <a href=\"https:\/\/benjiweber.co.uk\/blog\/2015\/08\/04\/lambda-type-references\/\" class=\"more-link\" title=\"Read Lambda Type References\">Read more &raquo;<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[8],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v14.9 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/benjiweber.co.uk\/blog\/2015\/08\/04\/lambda-type-references\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Lambda Type References - Benji&#039;s Blog\" \/>\n<meta property=\"og:description\" content=\"We&#8217;re used to type erasure ruining our day in Java. Want to create a new T() in a generic method? Normally not possible. We can pass a Class&lt;T&gt; around, but that doesn&#8217;t work for generic types. We can&#8217;t write List&lt;String&gt;.class One exception to this was using super type tokens. We can get the generic type... Read more &raquo;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/benjiweber.co.uk\/blog\/2015\/08\/04\/lambda-type-references\/\" \/>\n<meta property=\"og:site_name\" content=\"Benji&#039;s Blog\" \/>\n<meta property=\"article:published_time\" content=\"2015-08-04T07:25:34+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2015-08-16T20:27:07+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#website\",\"url\":\"https:\/\/benjiweber.co.uk\/blog\/\",\"name\":\"Benji&#039;s Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/benjiweber.co.uk\/blog\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/2015\/08\/04\/lambda-type-references\/#webpage\",\"url\":\"https:\/\/benjiweber.co.uk\/blog\/2015\/08\/04\/lambda-type-references\/\",\"name\":\"Lambda Type References - Benji&#039;s Blog\",\"isPartOf\":{\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#website\"},\"datePublished\":\"2015-08-04T07:25:34+00:00\",\"dateModified\":\"2015-08-16T20:27:07+00:00\",\"author\":{\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#\/schema\/person\/45ecb36b51f4ce99e6929d2d31ca5c09\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/benjiweber.co.uk\/blog\/2015\/08\/04\/lambda-type-references\/\"]}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#\/schema\/person\/45ecb36b51f4ce99e6929d2d31ca5c09\",\"name\":\"benji\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/05fb47b31a0b329e1b790074a9b624ef?s=96&d=mm&r=g\",\"caption\":\"benji\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/posts\/873"}],"collection":[{"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/comments?post=873"}],"version-history":[{"count":29,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/posts\/873\/revisions"}],"predecessor-version":[{"id":911,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/posts\/873\/revisions\/911"}],"wp:attachment":[{"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/media?parent=873"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/categories?post=873"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/tags?post=873"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}