3

I want to learn possibilities within "newer" syntax and API of Java. By newer I mean 10+ (let's say 10-13). It is mostly around declaration of lambdas and storing different implementations conforming to same signature as values in the map. As recently I work mostly with Gosu I could come with this snippet:

var longInput = 10000000L

new LinkedHashMap<String, block(long) : long>( {
    "byte"  -> (\x -> x as byte as long),
    "short" -> (\x -> x as short as long),
    "int"   -> (\x -> x as int as long),
    "long"  -> (\x -> x as long as long)
}).eachKeyAndValue(\key, value ->
  print("${longInput} ${value(longInput) == longInput ? "can" : "cannot"} be converted to ${key}")
)

I could do it similarly in Java 10:

import java.util.*;

public class Test {
    public static void main(String[] args) {
        long longInput = 10000000L;

        var conversions = new LinkedHashMap<String, Conversion<Long>>();
        conversions.put("byte",  (x) -> (long) (byte)  x.longValue());
        conversions.put("short", (x) -> (long) (short) x.longValue());
        conversions.put("int",   (x) -> (long) (int)   x.longValue());
        conversions.put("long",  (x) -> (long) (long)  x.longValue());

        conversions.forEach((key, value) -> {
            System.out.printf("%d %s be converted to %s%n", longInput, value.convert(longInput) == longInput ? "can" : "cannot", key);      
        });
    }
}

interface Conversion<T> {
    T convert(T input);
}

My questions:

  1. Could it be done without having a named interface but in similar 'anonymous' function way like in Gosu?
  2. Anything else that could make this more concise in Java?

Update: This is just some play around code which aim was to do double casting of primitive long to smaller types and back. Inspired by https://www.hackerrank.com/challenges/java-datatypes/problem. So from my point of view this I wanted to stay.

Using answers my current Java 10 code would look like this:

public class Test { 
    public static void main(String[] args) {
        var longInput = 10000000L;

        new LinkedHashMap<String, UnaryOperator<Long>>() {{
            put("byte",  (x) -> (long) (byte)  x.longValue());
            put("short", (x) -> (long) (short) x.longValue());
            put("int",   (x) -> (long) (int)   x.longValue());
            put("long",  (x) -> (long) (long)  x.longValue());
        }}.forEach((key, value) -> {
            System.out.printf("%d %s be converted to %s%n", longInput, value.apply(longInput) == longInput ? "can" : "cannot", key);        
        });
    }
}

Marek Pulka
  • 111
  • 1
  • 5
  • Don't know the answer directly, but note that you can move the interface definition into the `Test` class definition so it doesn't take up a file / distract from the functionality of the classes in your package. – Maarten Bodewes Dec 20 '19 at 13:55
  • That's right, thanks! @Naman suggested that indeed this interface is already in the library as java.util.function.UnaryOperator so this could be avoided. – Marek Pulka Dec 20 '19 at 15:14

1 Answers1

6

Could it be done without having a named interface but in similar 'anonymous' function way like in Gosu?

Java already has a FunctionalInterface similar to that you have defined. You can make use of UnaryOperator<Long> values inn your Map.

Anything else that could make this more concise in Java?

This would in my opinion read better:

Map<String, UnaryOperator<Long>> conversions = new LinkedHashMap<>();
conversions.put("byte", a -> (long) a.byteValue());
conversions.put("short", a -> (long) a.shortValue());
conversions.put("int", a -> (long) a.intValue());
conversions.put("long", a -> a); // UnaryOperator.identity()
Naman
  • 27,789
  • 26
  • 218
  • 353
  • Thanks for UnaryOperator. I see it is extension of Function interface that would already be good from the perspective of what I wanted to try. Especially in original form I was writing int -> boolean lambdas where Function more general form would be needed. Still I see that these concrete interfaces are needed to store it in the collection as there is no syntax support from the language yet. – Marek Pulka Dec 20 '19 at 15:00
  • Regarding the double casting it was made on purpose as it was for checking such casting behavior on primitive long (primitive long could not be used in Java generics so this is the reason for .longValue() call). Normally I agree these functions to return the needed primitive type or avoiding any casting in case of long. BTW my exercise was inspired by this simple problem https://www.hackerrank.com/challenges/java-datatypes/problem – Marek Pulka Dec 20 '19 at 15:06
  • 1
    @MarekPulka True, there has to be a type inferred for the representation. Even if you consider defining the Map with double brace initialization such as `var conversions = new LinkedHashMap>() {{ put("byte", a -> (long) a.byteValue()); put("short", a -> (long) a.shortValue()); put("int", a -> (long) a.intValue()); put("long", a -> a); }};`, you cannot use the method `byteValue()` or the likes unless they are at the `Object` class level. – Naman Dec 20 '19 at 15:07
  • @MarekPulka Note also that I have just reduced the first casting using `byteValue()`, `shortValue()` methods of the `Long` class and then casting it still to a primitive. – Naman Dec 20 '19 at 15:09
  • 1
    yes you are right. I forgot that this part was part of this 'what happens if' portion of the exercise so I agree it is not optimal but that was intentional. I added this remark into the question just now. – Marek Pulka Dec 20 '19 at 15:26
  • Thanks @Naman also for double bracket initialization. Not working with Java on day by day basis I forgot about this possibility. Now it looks very close to Gosu version. – Marek Pulka Dec 20 '19 at 15:32
  • 1
    @MarekPulka But while using it in Java, one should definitely read about the [Efficiency of Java “Double Brace Initialization”?](https://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization) – Naman Dec 20 '19 at 15:33
  • Yes, another good point. I remember reading it some time ago. In that particular case double braces not being most optimal having that small overhead is completely acceptable. Again this is because this is just artificial exercise limited to 4 (strange) lambdas. So I will leave it with double braces. – Marek Pulka Dec 20 '19 at 15:40