我经常想将一个列表映射到另一个列表.例如,如果我有一个人员列表,我想要一个他们的名字列表,我想这样做:
目标
List<Person> people = ... ;
List<String> names = people.map(x -> x.getName());
Run Code Online (Sandbox Code Playgroud)
使用Java 8可以实现这样的功能:
JAVA 8版
List<String> names = people.stream()
.map(x -> x.getName())
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
但这显然不是那么好.事实上,我认为使用番石榴更清洁:
GUAVA版本
List<String> names = Lists.transform(people, x -> x.getName());
Run Code Online (Sandbox Code Playgroud)
但是,我喜欢链接.那么,我的目标是否可行?
我听说有人说Java 8默认方法类似于C#扩展方法.使用C#扩展方法,我可以轻松地将辅助方法添加到IEnumerable<T>:
public static IEnumerable<TRet> Map<T, TRet>(this IEnumerable<T> list, Func<T, TRet> selector)
{
return list.Select(selector);
}
Run Code Online (Sandbox Code Playgroud)
但是,我无法弄清楚如何使用默认方法来扩展现有接口.
此外,这显然是一个微不足道的例子.通常,我希望能够扩展List<T>和Iterable<T>接口,以便更容易地与流api进行交互.
我正在使用JDiagram JAR,如下所示
Diagram myDigram = new Diagram();
myDigram.routeAllLinks();
Run Code Online (Sandbox Code Playgroud)
使用JRE 7运行时此代码可以正常工作,但是当它与JRE 8一起运行时,会抛出以下错误:
java.lang.StackOverflowError
at java.util.Collections.sort(Unknown Source)
at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)
Run Code Online (Sandbox Code Playgroud)
我跟着堆栈跟踪到JDiagram反编译代码.观察到routeAllLinks()在另一个对象(比如路由器)上调用RouteLinks(),并且在另一个级别深度调用错误堆栈跟踪中出现的ExtendedArrayList.sort().JDiagram中的"ExtendedArrayList"扩展了ArrayList,并包含一个名为"sort()"的方法,该方法具有以下定义.
public void sort(Comparator<? super T> paramComparator)
{
Collections.sort(this, paramComparator);
}
Run Code Online (Sandbox Code Playgroud)
在Google上,我发现JRE 8引入了List.sort()并将Collections.sort()调用委托给集合(在我的情况下为ExtendedArrayList)排序方法.因此,库ExtendedArrayList.sort()成为了一个覆盖.它会创建一个无限递归,从而导致stackoverflow.我现在可以用一小段代码重现这个问题.
也
我们尝试过以下目前无效的解决方案
如果有任何要点需要详细说明,请告诉我.欢迎任何帮助.谢谢.
更新 到目前为止,javassist是最好的方法,但JDiagram混淆是阻止解决方案正常工作.我们有点认为考虑到我们的发布日期是不可能的(不得不说).我们已经开始升级库的过程.同时从我们的应用程序中删除了一个由routeAllLinks()方法提供的小功能.. :-(感谢大家的帮助.我将继续研究这个问题,因为我发现它真的很有趣和挑战..我如果我能解决它,我会更新帖子.而且我将继续为@gontard提供他的javassist方法,因为我正在继续我的研究.谢谢.
Cloneable在Java中本质上是破碎的.具体来说,我对界面的最大问题是它需要一种不定义方法本身的方法行为.因此,如果遍历Cloneable列表,则必须使用反射来访问其定义的行为.但是,在Java 8中,我们现在有默认方法,现在我问为什么没有默认clone()方法Cloneable.
我理解为什么接口不能默认Object方法,但是,这是一个明确的设计决策,因此可以做出异常.
我有点想到弃用Object.clone()并将其内部代码更改为:
if(this instanceof Cloneable) {
return ((Cloneable) this).clone();
}
else {
throw new CloneNotSupportedException();
}
Run Code Online (Sandbox Code Playgroud)
并继续前进任何魔法使其clone()成为默认方法Cloneable.这并没有真正解决,clone()仍然可以很容易地错误地实现,但这本身就是另一个讨论.
据我所知,这种变化将完全向后兼容:
clone()但未实现的类Cloneable(为什么?!)在技术上仍然可以(即使在功能上不可能,但这与以前没有什么不同).clone()但实现的类Cloneable在其实现上仍将起相同的作用.clone(),但确实实现Cloneable(为什么?!)的类现在将遵循规范,即使它在功能上并不完全正确.Object.clone()仍然可以在功能上工作.super.clone()即使它正在引用,它在功能上仍然是相同的Object.clone().更不用说这将解决一个巨大的问题Cloneable.虽然繁琐且仍然容易错误地实现,但它将解决界面的巨大面向对象问题.
我能看到的唯一问题是那些实现Cloneable没有义务覆盖的问题clone(),但这与以前没有什么不同.
这已在内部进行过讨论,但从未取得成果吗?如果是这样,为什么?如果是因为接口不能默认使用Object方法,那么在这种情况下做出异常是否有意义,因为所有继承Cloneable的对象都是期待clone()的?
我认为它不能,因为标记接口原则是没有任何方法,但由于默认方法不是抽象的我不确定.
假设我有以下类层次结构:
interface Collection<E>
{
Collection<E> $plus(E element)
}
interface MutableCollection<E> extends Collection<E>
{
@Override
MutableCollection<E> $plus(E element)
}
interface Set<E> extends Collection<E>
{
@Override
Set<E> $plus(E element)
}
interface MutableSet<E> extends Set<E>, MutableCollection<E>
{
@Override
default MutableSet<E> $plus(E element)
{
// ... implementation
}
}
abstract class AbstractArraySet<E> implements Set<E>
{
// ... no $plus(...) implementation
}
class ArraySet<E> extends AbstractArraySet<E> implements MutableSet<E>
{
// ... no $plus(...) implementation
}
Run Code Online (Sandbox Code Playgroud)
如您所见,只有MutableSet该类提供了该$plus方法的实现.在测试用例中,我在类型的实例上调用此方法ArraySet.测试总是在CI环境中传递,而它总是AbstractMethodError在我的本地环境中失败.在这两种情况下,我都在使用Gradle(2.7).
java interface abstract-methods java-bridge-method default-method
Java 9即将推出,更多功能将添加到Java接口,如私有方法.default接口中的方法是在Java 8中添加的,主要是为了支持在集合中使用lambda,而不会破坏与该语言的早期版本的复古兼容性.
在Scala中,traits中的方法非常有用.但是,Scala使用不同的方法处理traits而不是Java default.考虑多重继承解决方案或使用traits作为mixin.
除了以上使用,哪些是使用default方法的真实场景值得?这几年是否出现了一些使用它们的模式?使用这种方法可以解决哪些问题?
我对Java 8中引入的接口中的默认方法实现感到有些困惑.我想知道是否应该专门为接口及其实现的方法编写JUnit测试.我试着谷歌,但我找不到一些指导方针.请指教.
通常承认,通过继承扩展接口的实现不是最佳实践,并且该组合(例如,从头开始再次实现接口)更加可维护.
这是有效的,因为接口契约迫使用户实现所有所需的功能.但是在java 8中,默认方法提供了一些可以"手动"覆盖的默认行为.请考虑以下示例:我想设计一个用户数据库,该数据库必须具有List的功能.出于效率目的,我选择通过ArrayList来支持它.
public class UserDatabase extends ArrayList<User>{}
Run Code Online (Sandbox Code Playgroud)
这通常不被认为是一种很好的做法,如果真的希望列表的全部功能并遵循通常的"遗产构成"的座右铭,我宁愿这样做:
public class UserDatabase implements List<User>{
//implementation here, using an ArrayList type field, or decorator pattern, etc.
}
Run Code Online (Sandbox Code Playgroud)
但是,如果不注意,则不需要覆盖某些方法,例如spliterator(),因为它们是List接口的默认方法.问题是,List的spliterator()方法执行得比ArrayList的spliterator()方法差得多,后者已针对ArrayList的特定结构进行了优化.
这迫使开发者
所以问题是:在这种情况下,人们应该更喜欢构成而不是继承吗?
学习java 8的默认方法.这个链接 就像互联网上的任何其他资源一样
在"最严格的意义上",默认方法是向后退一步,因为它们允许您使用代码"污染"您的接口.但它们提供了最优雅和实用的方式来允许向后兼容.它使Oracle更容易更新所有Collections类,并让您更新Lambda的现有代码.
我的理解是java 8 dev/designer在接口中提供了默认方法,因此所有实现类都不必不必要地覆盖相同的行为,因此提供向后兼容性.例如: - 如果ForEach方法不是默认方法,则实现类的每个集合都必须实现它.同意.
为了克服这个问题,我们可以让一个类提供这些默认方法的实现,然后实现类似arraylist等类可以扩展它.通过这种方式,我们可以对java基础知识(即可重用性和抽象性)进行统计,即保持界面污染较少
我相信java 8开发/设计师已经考虑过这一点,因为他们学到的东西更多,我在这里缺少一些东西.有人可以在这里帮助我们开发人员也可以作为这一重大变化吗?
可以说我有以下结构:
abstract class A {
abstract boolean foo();
}
interface B {
default boolean foo() { return doBlah(); }
}
class C extends A implements B {
//function foo
}
Run Code Online (Sandbox Code Playgroud)
Java现在会抱怨该类C必须实现抽象方法foo A.通过重新定义函数C并简单地调用,我可以相对容易地解决这个问题B.super.foo();.
但是我不明白为什么接口的默认函数B不能单独满足这个要求,我希望能更好地理解java的底层机制.
default-method ×10
java-8 ×9
java ×8
interface ×3
arraylist ×1
clone ×1
cloneable ×1
collections ×1
composition ×1
guava ×1
inheritance ×1
java-9 ×1
junit ×1
lambda ×1
unit-testing ×1