Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics with Function.apply

I am playing with Java utility functions. I have the following code:

public class Checker<T>{

  private T value;
  private Function<T, T> callback;

  private Checker(T value) {
    this.value = value;
  }

  public static Checker when(String o) {
    return new Checker<String>(o);
  }

  public static Checker when(int o) {
    return new Checker<Integer>(o);
  }

  public Checker then(Function<T, T> callback) {
    this.callback = callback;
    return this;
  }

  public void execute() {
    if (this.value instanceof String) {
      this.callback.apply("123");
    }
    if (this.value instanceof Integer) {
      this.callback.apply(123);
    }
  }

  Checker.when("123").then(str -> {
    return "";
  }).execute();

  Checker.when(123).then(str -> {
    return "";
  }).execute();

Now here I am getting an error for this.callback.apply("123") as it requires T and cannot cast it to String.

Is it possible to have generic return types for Function<T,T>? I can send T, but then its received as an Object in my lambda, but I want as String or Integer.

like image 304
Minato Namikaze Avatar asked Dec 11 '17 09:12

Minato Namikaze


3 Answers

I made some changes in your Checker class which make sense to me. I eliminated all the raw types, and I used the value member in execute. I added a return type of execute, in order to be able to print its result.

class Checker<T>{

  private T value;
  private Function<T, T> callback;

  private Checker(T value) {
    this.value = value;
  }

  public static Checker<String> when(String o) {
    return new Checker<>(o);
  }

  public static Checker<Integer> when(int o) {
    return new Checker<>(o);
  }

  public Checker<T> then(Function<T, T> callback) {
    this.callback = callback;
    return this;
  }

  public T execute() {
    return this.callback.apply(value);
  }

  public static void main (String[] args) {
    Checker.when("123").then(str -> {
      return "." + str + ".";
    }).execute();

    Checker.when(123).then(i -> {
      return i + 100;
    }).execute();
  }
}

Now when you check your class with:

System.out.println (Checker.when("123").then(str -> "." + str + ".").execute());
System.out.println (Checker.when(123).then(i -> i + 100).execute());

You get:

.123.
223
like image 187
Eran Avatar answered Oct 18 '22 01:10

Eran


This code can be even further simplified, like this:

static class Checker<T> {

    private final T value;

    private UnaryOperator<T> callback;

    private Checker(T value) {
        this.value = value;
    }

    public static <T> Checker<T> when(T o) {
        return new Checker<>(o);
    }

    public Checker<T> then(UnaryOperator<T> callback) {
        this.callback = callback;
        return this;
    }

    public T execute() {
        return this.callback.apply(value);
    }
}
like image 32
Eugene Avatar answered Oct 18 '22 00:10

Eugene


As the others suggested the code can be simplified, but why not make the calls chainable and also the class final, that way you'd have a simple and clean model:

final class Checker<T> {

    private final T value;

    private final UnaryOperator<T> callback;

    private Checker(T value, UnaryOperator<T> callback) {
        this.value = value;
        this.callback = callback;
    }

    public static <T> Checker<T> when(T t) {
        return new Checker<>(t, UnaryOperator.identity());
    }

    public Checker<T> then(UnaryOperator<T> callback) {
        return new Checker<>(value, t -> callback.apply(this.callback.apply(t)));
    }

    public T execute() {
        return callback.apply(value);
    }
}

Rather than returning this in the then() this approach returns a completly new Checker-instance

like image 31
Lino Avatar answered Oct 18 '22 00:10

Lino