2017-06-05 11 views
5

Utworzono klasy tuple i umieść je w kolekcji Java. Ale nie chcę używać tuple jako parametrów funkcji bezpośrednio podczas iterowania kolekcji. Więc zaimplementowałem rozpakowywanie krotek jako następujący kod.Jak zaimplementować rozpakowanie krotek dla parametrów funkcji w Javie?

zasadzie to działa, ale problemem jest to, że odlew typ jest potrzebne:

map((Func2<Long, Long, Long>) (a, b) -> a + b)

Czy istnieje jakiś sposób, aby usunąć casting typu tutaj?


edit:

Może nie było jasne, nie tylko tuple2, ale także tuple3, tuple4 ... powinny być wspierane. @ Flown na odpowiedź działa znacznie dla Tuple2, ale nie działa na tuple2, tuple3, tuple4 w międzyczasie

package test; 


import com.google.common.collect.Iterables; 

import java.util.Arrays; 
import java.util.function.Function; 

import static test.TupleIterable.Tuple.tuple; 

public interface TupleIterable<T> { 

    Iterable<T> apply(); 

    static <E> TupleIterable<E> from(Iterable<E> iterable) { 
     return() -> iterable; 
    } 

    default <E> TupleIterable<E> map(Function<? super T, ? extends E> op) { 
     return() -> Iterables.transform(TupleIterable.this.apply(), op::apply); 
    } 

    interface Func2<T1, T2, R> extends Function<Tuple.Tuple2<T1, T2>, R> { 

     R apply(T1 t1, T2 t2); 

     @Override 
     default R apply(Tuple.Tuple2<T1, T2> t) { 
      return apply(t.t1, t.t2); 
     } 
    } 

    interface Func3<T1, T2, T3, R> extends Function<Tuple.Tuple3<T1, T2, T3>, R> { 

     R apply(T1 t1, T2 t2, T3 t3); 

     @Override 
     default R apply(Tuple.Tuple3<T1, T2, T3> t) { 
      return apply(t.t1, t.t2, t.t3); 
     } 
    } 

    interface Tuple { 

     static <T1, T2> Tuple2<T1, T2> tuple(T1 t1, T2 t2) { 
      return new Tuple2<>(t1, t2); 
     } 

     static <T1, T2, T3> Tuple3<T1, T2, T3> tuple(T1 t1, T2 t2, T3 t3) { 
      return new Tuple3<>(t1, t2, t3); 
     } 

     class Tuple2<T1, T2> implements Tuple { 

      public T1 t1; 

      public T2 t2; 

      public Tuple2(T1 t1, T2 t2) { 
       this.t1 = t1; 
       this.t2 = t2; 
      } 
     } 

     class Tuple3<T1, T2, T3> implements Tuple { 

      public T1 t1; 

      public T2 t2; 

      public T3 t3; 

      public Tuple3(T1 t1, T2 t2, T3 t3) { 
       this.t1 = t1; 
       this.t2 = t2; 
       this.t3 = t3; 
      } 
     } 
    } 

    public static void main(String[] args) { 
     TupleIterable.from(Arrays.asList(1L, 2L)) 
       .map(x -> tuple(x, x)) // map long to tuple2 
       .map((Func2<Long, Long, Tuple.Tuple3<Long, Long, Long>>) (a, b) -> tuple(a, b, a + b)) // map tuple2 to tuple3 
       .map((Func3<Long, Long, Long, Long>) (a, b, c) -> a + b + c) // map tuple3 to Long 
       .apply() 
       .forEach(System.out::println); 
    } 
} 

Odpowiedz

3

Można zrobić taką samą sztuczkę jako deweloper JDK zrobił dla Stream specjalizacji prymitywnych typów. Wprowadź mapToTuple i unwrap w różnych interfejsach.

Przepisałem część Twojego kodu i użyłem istniejącego FunctionalInterfaces.

import java.util.Arrays; 
import java.util.Iterator; 
import java.util.function.BiFunction; 
import java.util.function.Function; 
import java.util.function.Supplier; 

public class Test { 

    public static void main(String... args) { 
    MyIterable.from(Arrays.asList(1L, 2L)).mapToTuple(l -> Tuple2.tuple(l, l + 1L)).unwrap((a, b) -> a + b).get() 
     .forEach(System.out::println); 
    } 
} 

final class Tuple2<T1, T2> { 

    public static <T1, T2> Tuple2<T1, T2> tuple(T1 t1, T2 t2) { 
    return new Tuple2<>(t1, t2); 
    } 

    public final T1 t1; 
    public final T2 t2; 

    private Tuple2(T1 t1, T2 t2) { 
    this.t1 = t1; 
    this.t2 = t2; 
    } 
} 

@FunctionalInterface 
interface TupleIterable<T1, T2> extends Supplier<Iterable<Tuple2<T1, T2>>> { 

    default <E> MyIterable<E> unwrap(BiFunction<T1, T2, E> func) { 
    return() -> Iterables.transform(get(), t -> func.apply(t.t1, t.t2)); 
    } 
} 

@FunctionalInterface 
interface MyIterable<T> extends Supplier<Iterable<T>> { 

    static <E> MyIterable<E> from(Iterable<E> iterable) { 
    return() -> iterable; 
    } 

    default <E> MyIterable<E> map(Function<? super T, ? extends E> mapper) { 
    return() -> Iterables.transform(get(), mapper::apply); 
    } 

    default <T1, T2> TupleIterable<T1, T2> mapToTuple(Function<? super T, ? extends Tuple2<T1, T2>> tupleMapper) { 
    return() -> Iterables.transform(get(), tupleMapper::apply); 
    } 
} 

final class Iterables { 
    public static <T, E> Iterable<E> transform(Iterable<T> iterable, Function<? super T, ? extends E> mapper) { 
    return() -> new Iterator<E>() { 

     private final Iterator<T> iter = iterable.iterator(); 

     @Override 
     public boolean hasNext() { 
     return iter.hasNext(); 
     } 

     @Override 
     public E next() { 
     return mapper.apply(iter.next()); 
     } 
    }; 
    } 
} 
+0

Dziękuję za wspaniałą odpowiedź! Mam inne pytanie, czy to wspiera inne krotki, takie jak 'Tuple3',' Tuple4', etc ... Wydaje się, że muszę stworzyć wiele metod, takich jak 'mapToTuple3',' mapToTuple4' ... – moshangcheng

+0

'Tuple3Iterable ',' Tuple4Iterable' nie są ze sobą kompatybilne. To znaczy, argumenty nie mogą być rozpakowane w 'Tuple3Iterable :: mapToTuple4' – moshangcheng

+2

@moshangcheng Jeśli chcesz być taki ogólny, nie możesz uniknąć rzucania. – Flown

0

naprawdę nie mogę powiedzieć, czy jest to zbyt „brudny”, czy nie, ale dlaczego nie zapewniają pobierające dla T1 i T2 wewnątrz Tuple2 i zrobić:

.map(x -> tuple(x, x + 1)) // map long to tuple2 
.map(a -> a.getT1() + a.getT2()) // map tuple2 to long 
1

Zamiast robić typu odlew, Stworzyłem funkcję unpack do rozpakowania krotek. Działa jak , choć wciąż mało intuicyjna.

package test; 


import com.google.common.collect.Iterables; 

import java.util.Arrays; 
import java.util.function.Function; 

import static test.TupleIterable.Func.unpack; 
import static test.TupleIterable.Tuple.tuple; 

public interface TupleIterable<T> { 

    Iterable<T> apply(); 

    static <E> TupleIterable<E> from(Iterable<E> iterable) { 
     return() -> iterable; 
    } 

    default <E> TupleIterable<E> map(Function<? super T, E> op) { 
     return() -> Iterables.transform(TupleIterable.this.apply(), op::apply); 
    } 

    interface Func { 

     static <T1, T2, R> Function<Tuple.Tuple2<T1, T2>, R> unpack(Func2<T1, T2, R> f) { 
      return t -> f.apply(t.t1, t.t2); 
     } 

     static <T1, T2, T3, R> Function<Tuple.Tuple3<T1, T2, T3>, R> unpack(Func3<T1, T2, T3, R> f) { 
      return t -> f.apply(t.t1, t.t2, t.t3); 
     } 

     @FunctionalInterface 
     interface Func2<T1, T2, R> { 

      R apply(T1 t1, T2 t2); 
     } 

     @FunctionalInterface 
     interface Func3<T1, T2, T3, R> { 

      R apply(T1 t1, T2 t2, T3 t3); 
     } 
    } 

    interface Tuple { 

     static <T1, T2> Tuple2<T1, T2> tuple(T1 t1, T2 t2) { 
      return new Tuple2<>(t1, t2); 
     } 

     static <T1, T2, T3> Tuple3<T1, T2, T3> tuple(T1 t1, T2 t2, T3 t3) { 
      return new Tuple3<>(t1, t2, t3); 
     } 

     class Tuple2<T1, T2> implements Tuple { 

      public T1 t1; 

      public T2 t2; 

      public Tuple2(T1 t1, T2 t2) { 
       this.t1 = t1; 
       this.t2 = t2; 
      } 
     } 

     class Tuple3<T1, T2, T3> implements Tuple { 

      public T1 t1; 

      public T2 t2; 

      public T3 t3; 

      public Tuple3(T1 t1, T2 t2, T3 t3) { 
       this.t1 = t1; 
       this.t2 = t2; 
       this.t3 = t3; 
      } 
     } 
    } 

    public static void main(String[] args) { 
     TupleIterable.from(Arrays.asList(1L, 2L)) 
       .map(x -> tuple(x, x)) // map long to tuple2 
       .map(unpack((a, b) -> tuple(a, b, a + b))) // map tuple2 to tuple3 
       .map(unpack((a, b, c) -> a + b + c)) // map tuple3 to Long 
       .apply() 
       .forEach(System.out::println); 
    } 
} 
Powiązane problemy