How to declare method's return type the as return type of last lambda in array passed to the method

前端 未结 3 573
清酒与你
清酒与你 2021-01-05 19:51

I ask for something which I see impossible and I\'ll delete question if it is.

I have got method:

public Object convertBy(Function... functions) {
}
         


        
相关标签:
3条回答
  • 2021-01-05 19:58

    You have to change the method signature and inline the last vararg value as a separate parameter.

    If you have this parameter as the last one, then you won't be able a use vararg parameter, as it has always to be last one and must be represented as an array in case it's not the last one:

    public <T, R> R convertBy(Function[] functions, Function<T, R> special) { }
    

    If you, however, insist to use varargs, then you can move the "special" Function as first parameter:

    public <T, R> R convertBy(Function<T, R> special, Function... functions) { }
    
    0 讨论(0)
  • 2021-01-05 20:17

    Thank all of you who elaborated on the subject, your solutions are much better in real world.

    As the author I would like to post my solution that enabled not changing the invocations of convertBy() int the main() one bit. It is very short and ugly, but works.

    Main:

    Function<String, List<String>> flines ... lambda here
    
    Function<List<String>, String> join ... lambda here
    
    Function<String, List<Integer>> collectInts ... lambda here
    
    Function<List<Integer>, Integer> sum ... lambda here
    
    
    String fname = System.getProperty("user.home") + "/LamComFile.txt"; 
    InputConverter<String> fileConv = new InputConverter<>(fname);
    List<String> lines = fileConv.convertBy(flines);
    String text = fileConv.convertBy(flines, join);
    List<Integer> ints = fileConv.convertBy(flines, join, collectInts);
    Integer sumints = fileConv.convertBy(flines, join, collectInts, sum);
    
    System.out.println(lines);
    System.out.println(text);
    System.out.println(ints);
    System.out.println(sumints);
    
    List<String> arglist = Arrays.asList(args);
    InputConverter<List<String>> slistConv = new InputConverter<>(arglist);  
    sumints = slistConv.convertBy(join, collectInts, sum);
    System.out.println(sumints); 
    

    The InputConverter class:

    public class InputConverter<T> {
    
        private T value;
    
        public InputConverter(T value) {
            this.value = value;
        }
    
        public <T, R> R convertBy(Function... functions) {
            Object result = value;
            for (int i = 0; i < functions.length; i++) {
                result = functions[i].apply(result);
            }
            return (R) result;
        }
    }
    
    0 讨论(0)
  • 2021-01-05 20:22

    It seems, you have some misunderstanding about generic type hierarchies. When you want to extend a generic type, you have to make a fundamental decision about the actual types of the extended class or interface. You may specify exact types like in

    interface StringTransformer extends Function<String,String> {}
    

    (here we create a type that extends a generic type but is not generic itself)

    or you can create a generic type which uses its own type parameter for specifying the actual type argument of the super class:

    interface NumberFunc<N extends Number> extends Function<N,N> {}
    

    Note, how we create a new type parameter N with its own constraints and use it to parametrize the superclass to require its type parameters to match ours.

    In contrast, when you declare a class like

    interface FLines<T, R> extends Function
    

    you are extending the raw type Function and create new type parameters <T, R> which are entirely useless in your scenario.

    To stay at the above examples, you may implement them as

    StringTransformer reverse = s -> new StringBuilder(s).reverse().toString();
    NumberFunc<Integer> dbl = i -> i*2;
    

    and since they inherit properly typed methods, you may use these to combine the functions:

    Function<String,Integer> f = reverse.andThen(Integer::valueOf).andThen(dbl);
    System.out.println(f.apply("1234"));
    

    Applying this to your scenario, you could define the interfaces like

    interface FLines extends Function<String,List<String>> {
        @Override default List<String> apply(String fileName) {
            return getLines(fileName);
        }        
        public List<String> getLines(String fileName);
    }
    interface Join extends Function<List<String>,String> {
        @Override default String apply(List<String> lines) {
            return join(lines);
        }
        public String join(List<String> lines);
    }
    interface CollectInts extends Function<String,List<Integer>> {
        @Override default List<Integer> apply(String s) {
            return collectInts(s);
        }
        public List<Integer> collectInts(String s);
    }
    interface Sum extends Function<List<Integer>, Integer> {
        @Override default Integer apply(List<Integer> list) {
            return sum(list);
        }
        public Integer sum(List<Integer> list);
    }
    

    and redesign your InputConverter to accept only one function which may be a combined function:

    public class InputConverter<T> {
    
        private T value;
    
        public InputConverter(T value) {
            this.value = value;
        }
        public <R> R convertBy(Function<? super T, ? extends R> f) {
            return f.apply(value);
        }
    }
    

    This can be used in a type safe manner:

    FLines flines = name -> {
        try { return Files.readAllLines(Paths.get(name)); }
        catch(IOException ex) { throw new UncheckedIOException(ex); }
    };
    Join join = list -> String.join(",", list);
    CollectInts collectInts=
        s -> Arrays.stream(s.split(",")).map(Integer::parseInt).collect(Collectors.toList());
    Sum sum = l -> l.stream().reduce(0, Integer::sum);
    
    InputConverter<String> fileConv = new InputConverter<>("LamComFile.txt");
    List<String> lines = fileConv.convertBy(flines);
    String text = fileConv.convertBy(flines.andThen(join));
    List<Integer> ints = fileConv.convertBy(flines.andThen(join).andThen(collectInts));
    Integer sumints = fileConv.convertBy(
        flines.andThen(join).andThen(collectInts).andThen(sum)
    );
    
    0 讨论(0)
提交回复
热议问题