我试图在我的代码中使用Java 8方法引用.有四种类型的方法参考可用.
随着Static method reference和Constructor reference我有没有问题,但Instance Method (Bound receiver)和Instance Method (UnBound receiver)真搞糊涂了.在Bound接收器中,我们使用Object引用变量来调用方法,如:
objectRef::Instance Method
Run Code Online (Sandbox Code Playgroud)
在UnBound接收器中,我们使用类名来调用方法,如:
ClassName::Instance Method.
Run Code Online (Sandbox Code Playgroud)
我有以下问题:
Bound和Unbound接收方法引用有什么区别?Bound在哪里使用Unbound接收器?我们应该在哪里使我还从Java 8语言特性书中找到了解释Bound和Unbound接收器,但仍然与实际概念混淆.
我错过了什么?为什么我必须在Object::toString下面使用而不是Integer::toString?它与带有泛型的类型擦除有什么关系吗?
Arrays.asList(1,2,3).stream().map(Integer::toString).forEach(System.out::println); //Won't compile
Arrays.asList(1,2,3).stream().map(Object::toString).forEach(System.out::println); //Compiles and runs fine
Run Code Online (Sandbox Code Playgroud) Java如何知道String::compareTo调用时使用哪个方法引用Collections.sort(someListOfStrings, String::compareTo);?compareTo不是静态的,它需要知道value比较的"左手边".
下面的代码编译正常但在运行时抛出异常.这是预期的行为吗?为什么?
码:
public static void main(String[] args) {
A<Integer> a = new A<> ();
System.out.println(a.min()); //prints null as expected
System.out.println(a.max()); //throws exception
}
static class A<T extends Number & Comparable<? super T>> {
Stream<T> s = Stream.empty();
public T min() { return s.min((t1, t2) -> t1.compareTo(t2)).orElse(null); }
public T max() { return s.max(T::compareTo).orElse(null); }
}
Run Code Online (Sandbox Code Playgroud)
输出:
null
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
at abc$A.max(abc.java:19)
at abc.main(abc.java:8)
Caused by: java.lang.invoke.LambdaConversionException: Invalid …Run Code Online (Sandbox Code Playgroud) 我已经在Java 8中使用lambdas和方法引用了一段时间,有一件事我不明白.这是示例代码:
Set<Integer> first = Collections.singleton(1);
Set<Integer> second = Collections.singleton(2);
Set<Integer> third = Collections.singleton(3);
Stream.of(first, second, third)
.flatMap(Collection::stream)
.map(String::valueOf)
.forEach(System.out::println);
Stream.of(first, second, third)
.flatMap(Set::stream)
.map(String::valueOf)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
两个流管道做同样的事情,他们打印出三个数字,每行一个.不同之处在于它们的第二行,似乎只要它具有方法就可以简单地替换继承层次结构中的类名(Collection接口具有默认方法"stream",而未在Set接口中重新定义).我尝试了如果使用这些类一次又一次地重新定义方法会发生什么:
private static class CustomHashSet<E> extends HashSet<E> {
@Override
public Stream<E> stream() {
System.out.println("Changed method!");
return StreamSupport.stream(spliterator(), false);
}
}
private static class CustomCustomHashSet<E> extends CustomHashSet<E> {
@Override
public Stream<E> stream() {
System.out.println("Changed method again!");
return StreamSupport.stream(spliterator(), false);
}
}
Run Code Online (Sandbox Code Playgroud)
在更改第一,第二和第三个赋值以使用这些类之后,我可以替换方法引用(CustomCustomHashSet :: stream),并且毫不奇怪它们在所有情况下都打印出调试消息,即使我使用Collection :: stream也是如此.看来你不能用方法引用调用super,overriden方法.
运行时间有差异吗?什么是更好的做法,请参考顶级接口/类或使用具体的已知类型(Set)?谢谢!
编辑:为了清楚,我知道继承和LSP,我的困惑与Java 8中方法引用的设计有关.我首先想到的是,在方法引用中更改类会改变行为,它会调用它来自所选类的超级方法,但正如测试所示,它没有任何区别.更改创建的实例类型确实会改变行为.
我打电话时遇到异常metafactory.它说:
java.lang.invoke.LambdaConversionException:
Incorrect number of parameters for instance method
invokeVirtual my.ExecuteTest$AProcess.step_1:()Boolean;
0 captured parameters,
0 functional interface method parameters,
0 implementation parameters
Run Code Online (Sandbox Code Playgroud)
我不明白所有的文件LambdaMetafactory.metafactory.我在确定正确的参数时遇到了问题:
所以它归结为以下两者之间的区别:
我的代码是这样的:
package my;
import java.lang.invoke.*;
import java.lang.reflect.Method;
public class Execute {
public interface ProcessBase {};
@FunctionalInterface
public interface Step {
Boolean apply(); …Run Code Online (Sandbox Code Playgroud) 我正在使用Groovy它JUnit来测试我的Java代码.
我需要测试的方法foo()这需要在java.util.function.Function
public void foo(Function<Foo,Bar> func){
return null;
}
Run Code Online (Sandbox Code Playgroud)
在我的正常代码中,我foo通过传入方法的方法引用来调用,bar即.
foo(mybar::bar)
Run Code Online (Sandbox Code Playgroud)
我怎样才能Groovy优雅地测试这个功能?
使用:
mybar.&bar
Run Code Online (Sandbox Code Playgroud)
产生groovy.lang.Closure<...>与之不相容的java.util.function.Function.
我怎么能做到这一点?
以下是我的代码段的两行:
List<String> listDevs = Arrays.asList("alvin", "Alchemist", "brutus", "larsen", "jason", "Kevin");
listDevs.sort(Comparator.comparing(String::length)); //This works fine
listDevs.sort(String::compareToIgnoreCase); //This works fine
Run Code Online (Sandbox Code Playgroud)
但是(当expermient)当我尝试写
listDevs.sort(Comparator.comparing(String::compareToIgnoreCase));
Run Code Online (Sandbox Code Playgroud)
编译器抛出错误
无法从类型String中对非静态方法compareToIgnoreCase(String)进行静态引用
类似的情况发生在下面的代码中
listDevs.sort(Comparator.comparing(String::compareTo));
Run Code Online (Sandbox Code Playgroud)
我理解错误,如果我删除Comparator.comparing(如上所示)它工作正常.
但我的观点是,这条线如何运作?
listDevs.sort(Comparator.comparing(字符串::长度));
我相信我错过了一些东西.我已经读过这个帖子了.这是同样的情况吗?
转换对象的列表Foo具有id,向Map<Integer,Foo>与该id关键,是容易使用流API:
public class Foo{
private Integer id;
private ....
getters and setters...
}
Map<Integer,Foo> myMap =
fooList.stream().collect(Collectors.toMap(Foo::getId, (foo) -> foo));
Run Code Online (Sandbox Code Playgroud)
有没有办法替换lambda表达式:(foo) -> foo使用::运算符的东西?就像是Foo::this
我有以下无法编译的程序:
只是块1编译正常并按预期工作 - 我可以有条件地选择一个对象并在其上内联调用一个方法.
只是块2也编译好并按预期工作 - 我可以有条件地为Supplier<String>变量分配方法引用并调用.get()该变量.
但是块3无法编译:
Lambda.java:31: error: method reference not expected here
String res = ((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar)).get();
^
Lambda.java:31: error: method reference not expected here
String res = ((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar)).get();
Run Code Online (Sandbox Code Playgroud)
我认为该想法在块1组合和2 I将能够为一体的类型执行块3 ((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar))是Supplier<String>.
import java.util.function.Supplier;
class Lambda {
private final String s;
private Lambda(String s) {
this.s = s;
}
private …Run Code Online (Sandbox Code Playgroud) method-reference ×10
java ×9
java-8 ×9
lambda ×3
comparator ×1
generics ×1
groovy ×1
reflection ×1