默认方法是我们的Java工具箱中一个不错的新工具.但是,我尝试编写一个定义default该toString方法版本的接口.Java告诉我这是禁止的,因为声明的方法java.lang.Object可能不会被default编辑.为什么会这样?
我知道存在"基类永远胜利"规则,因此默认情况下(pun;),方法的任何default实现Object都会被方法覆盖Object.但是,我认为没有理由说明Object规范中的方法不应该有例外.特别是对于toString具有默认实现可能非常有用.
那么,Java设计者决定不允许default方法覆盖方法的原因是什么Object?
由于已经知道它很容易地添加序列化支持lambda表达式时,目标接口已经不继承Serializable,只是喜欢(TargetInterface&Serializable)()->{/*code*/}.
我问,是一种反其道而行之,明确删除支持串行当目标接口不继承Serializable.
由于您无法从类型中删除接口,因此基于语言的解决方案可能看起来像(@NotSerializable TargetInterface)()->{/* code */}.但据我所知,没有这样的解决方案.(纠正我,如果我错了,这将是一个完美的答案)
即使在类实现时拒绝序列化是Serializable过去的合法行为,并且程序员控制下的类,模式看起来如下:
public class NotSupportingSerialization extends SerializableBaseClass {
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
throw new NotSerializableException();
}
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
throw new NotSerializableException();
}
private void readObjectNoData() throws ObjectStreamException {
throw new NotSerializableException();
}
}
Run Code Online (Sandbox Code Playgroud)
但是对于lambda表达式,程序员没有对lambda类的控制.
为什么有人会费心去除支持?好吧,除了生成包含Serialization支持的更大代码之外,它还会产生安全风险.请考虑以下代码:
public class CreationSite {
public static void main(String... arg) {
TargetInterface f=CreationSite::privateMethod;
} …Run Code Online (Sandbox Code Playgroud) 如果我使用新语法获取方法引用:
anObject::aMethod
Run Code Online (Sandbox Code Playgroud)
我总是得到同一个对象吗?也就是说,我可以相信对同一方法的两个引用是相同的吗?
例如,我很乐意知道是否计划将它们用作Runnable我可以添加和删除的回调:
someLibrary.addCallback(anObject::aMethod)
// later
someLibrary.removeCallback(sameObject::sameMethod)
Run Code Online (Sandbox Code Playgroud)
这是否需要在Runnable变量中保存引用以保持其稳定?
以下测试失败
@Test
public void test() {
Function<String, Integer> foo = Integer::parseInt;
Function<String, Integer> bar = Integer::parseInt;
assertThat(foo, equalTo(bar));
}
Run Code Online (Sandbox Code Playgroud)
有没有办法让它通过?
编辑:我会尝试更清楚地说明我正在尝试做什么.
可以说我有这些课程:
class A {
public int foo(Function<String, Integer> foo) {...}
}
class B {
private final A a; // c'tor injected
public int bar() {
return a.foo(Integer::parseInt);
}
}
Run Code Online (Sandbox Code Playgroud)
现在让我说我想为B编写单元测试:
@Test
public void test() {
A a = mock(A.class);
B b = new B(a);
b.bar();
verify(a).foo(Integer::parseInt);
}
Run Code Online (Sandbox Code Playgroud)
问题是测试失败,因为方法引用不相等.
为什么下面的片段在第二次传递时打印为真?它应该不是一个新的实例吗?
import java.util.function.Supplier;
public class Foo {
public static void main(String[] args) throws Exception {
Supplier<Long> old = () -> System.nanoTime();
for (int i = 0; i < 3; i++) {
/* false true true
Supplier<Long> foo = System::nanoTime;*/
Supplier<Long> foo = () -> System.nanoTime();
/* false false false
Supplier<Long> foo = new Supplier<Long>() {
@Override
public Long get() {
return System.nanoTime();
}
};
//*/
System.out.printf("%s %s %s%n", foo == old, foo, old);
old = foo;
}
}
}
Run Code Online (Sandbox Code Playgroud)
false …Run Code Online (Sandbox Code Playgroud) 我想知道,在Java-8中实现alpha等价比较是否有任何好的方法(如果可能的话)?
显然,这两个lambda-s是α等价的.让我们假设在某些情况下我们想要发现这个事实.如何实现?
Predicate<Integer> l1 = x -> x == 1;
Predicate<Integer> l2 = y -> y == 1;
Run Code Online (Sandbox Code Playgroud) 我想知道在没有太多接口的情况下在 Java 中实现某种 observable 的好方法是什么。
我认为使用预定义的功能接口会很好。在这个例子中,我使用 aString Consumer来表示一个接受一个字符串作为通知的监听器。
class Subject {
List<Consumer<String>> listeners = new ArrayList<>();
void addListener(Consumer<String> listener) { listeners.add(listener); }
void removeListener(Consumer<String> listener { listeners.remove(listener); }
...
}
class PrintListener {
public void print(String s) { System.out.println(s); }
}
Subject subject = new ...
PrintListener printListener = new ...
subject.add(printListener); // Works, I find it in the listener list
subject.remove(printListener); // Does NOT work. I still find it in the list
Run Code Online (Sandbox Code Playgroud)
我找到了解释:
Consumer<String> a = printListener::print; …Run Code Online (Sandbox Code Playgroud) 我不确定如何确定功能接口的相等/不变性.我想在java 8中使用这个语法糖时可能无法保证平等,如果你有任何提示,请告诉我任何提示.
我为我的问题制作了一个简短的代码片段.
public interface Element {
void doSomething(int a);
}
Run Code Online (Sandbox Code Playgroud)
我试图以功能方式添加此接口的实例
public class FunctionSet {
public void doubleUp(int a) {
System.out.println(a*2);
}
public void square(int a) {
System.out.println(a*a);
}
public static void main(String[] args) {
HashSet<Element> set = new HashSet<>();
FunctionSet functionSet = new FunctionSet();
set.add(functionSet::doubleUp);
set.add(functionSet::square);
System.out.println(set.add(functionSet::doubleUp));
}
}
Run Code Online (Sandbox Code Playgroud)
它打印为true,这意味着没有任何相等检查,并且我添加它后也无法从Set中删除任何实例.
如果我使用功能接口作为参数,有什么方法可以以某种方式比较这些实例?
将提供任何帮助,谢谢!
刚刚介绍Streams和Java 8 Lambda功能,以及对其他不言自明的Oracle doc Lambda Expressions的最后评论:
如果lambda表达式的目标类型及其捕获的参数是可序列化的,则可以序列化它.但是,与内部类一样,强烈建议不要对lambda表达式进行序列化.
检查这一点我发现了SO问题
OP处理来自客户端代码的序列化lambda表达式.
如果我有一个互联网服务和参数之一是一个lambda表达式,现在看来,这可能包含恶意代码,可以做这样的事情的文件系统访问权限,或导致堆栈溢出 - 所以这将是非常愚蠢的信任它.
我是否过度夸大了安全风险,或者序列化表达式可以包含什么限制?
Java 8引入了lambda表达式,这是一件好事.但现在考虑重写这段代码:
class B implements PropertyChangeListener {
void listenToA(A a) {
a.addPropertyChangeListener(this);
}
void propertyChange(PropertyChangeEvent evt) {
switch(evt.getPropertyName()) {
case "Property1":
doSomething();
break;
case "Property2":
doSomethingElse(); case "Property1":
doSomething;
break;
break;
}
void doSomething() { }
void doSomethingElse() { }
}
class A {
final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}
Run Code Online (Sandbox Code Playgroud)
随着lambda表达式和方法的引用,它已不再是需要有B落实PropertyChangeListner,我们可以写
class B {
void listenToA(A a) {
// using method …Run Code Online (Sandbox Code Playgroud) 我正在尝试构建一个库,您可以在其中添加和删除pub/sub系统中的事件的侦听器,但是使用方法引用遇到问题:
// here, this::printMessage is being passed as an instance of Consumer<String>
pubSub.subscribe(this::printMessage);
pubSub.unsubscribe(this::printMessage);
Run Code Online (Sandbox Code Playgroud)
在内部,调用subscribe()会将实例添加Consumer<T>到a Set<Consumer<T>>,unsubscribe()并将其删除.这个问题源于this::printMessage这里的每次使用实际上都会导致编译器生成一个新的对象引用/实例,因此,取消订阅实际上并不起作用.
到目前为止,我管理的解决方法是:
final Consumer<String> consumer = this::printMessage;
pubSub.subscribe(consumer);
pubSub.unsubscribe(consumer);
Run Code Online (Sandbox Code Playgroud)
但是,这并不是很理想.我担心的是,使用这个库的经验较少的人可能会认为他们可以在订阅/取消订阅时直接使用方法引用,但实际情况并非如此,最坏的情况是导致内存泄漏.
所以问题是,是否有一些聪明的方法来避免这种情况或强制方法引用总是解析为同一个对象引用/实例?
是否可以做类似的事情:
boolean isItMyMethod( Consumer<Object> aConsumer )
{
return aConsumer.equals( this::myMethod );
}
Run Code Online (Sandbox Code Playgroud)
这不能编译。如果我将 this::myMethod 分配给中间变量,它会起作用,但结果始终为 false。
java ×12
java-8 ×9
lambda ×6
equality ×1
immutability ×1
interface ×1
listener ×1
methodhandle ×1