我真的很喜欢编写和阅读lambda表达式-它们简洁,富于表现力和时尚(来吧,这样就没关系了!)。 将此与匿名类进行比较。 这就是为什么我喜欢摆脱它们!
在过去的几个月中,这种认识慢慢地实现了,昨天,我的潜意识对如何实现这一想法提出了质疑。 我将在这里展示它,并在尝试后的几周内发布后续信息。
总览
为了确保每个人都知道我们在说什么,我将快速回顾一下匿名类。 然后,我将解释为什么在确定他们的最后一个据点以及如何征服它之前要摆脱它们。
匿名类快速回顾
匿名类用于创建接口或抽象类的临时实现,如下所示:
异端阶级的例子
Runnable run = new Runnable() {@Overridepublic void run() {runThisThing(someArgument);}
};
确实确实创建了一个单独的类(您将在包含该代码的类旁边找到它的.class文件),但是由于它没有名称,因此您猜到了它的名称,它被称为匿名类。 我对此的观点始终是,这些课程应该确实很短。 一种,可能有两种方法,有两行。 凡是更长的东西,而且绝对是带有状态的东西,似乎都应该拥有自己的名称和位置–在文件的底部作为嵌套类甚至是自己的名称。 它总是使我无法读取方法,该方法有时会创建10+行的who-knows-what实现,而该实现完全无关。 但是对于简短的实现(如上面的示例),匿名类是最佳选择。
那他们怎么了?
匿名类没有什么错 。 只是在使用lambda表达式和方法/构造函数引用大约一年后,它们似乎是如此笨拙。 我越习惯于简单而准确地表达自己的行为,当面对匿名课堂的仪式和迷惑时,我就越被排斥。
只需将其与上面的示例进行比较:
异端阶级的例子
Runnable run = () -> runThisThing(someArgument);
在过去的几个月中,我逐渐意识到自己不想再看到它们了,昨天我对如何摆脱(必须知道的)必要的剩余事件有了一个很好的想法。
摆脱匿名类
如上所述,我认为比一个或两个方法的简单实现更为复杂的所有事物通常都应以嵌套或独立类的形式获得自己的名称和位置。
(顺便说一句,我倾向于对覆盖现有超类方法以更改其行为的类进行同样的操作。这可能很短,但是如果您不知道现在已被覆盖的原始代码,发现差异并推导意图通常很困难。在大多数情况下,给班级一个好名字可以解决这个问题。)
然后,当然有了Java 8,由于使用了lambda表达式,大量匿名类的用例就消失了。 这很棒! 它也是摆脱它们最后一个据点的工具:“具有几乎所有功能的”接口以及具有一个或两个抽象方法的抽象类的实现。
所以这是我的主意:
当遇到适合自己实现的接口或抽象类时,我们将创建一个功能实现 。 这是一个非抽象类,它将所有方法调用委托给在构造期间指定的功能接口。
例
我猜一个例子可以澄清这一点:
“几乎功能”界面
public interface ValueListener<T> {void invalidated(T formerValue);void changed(T formerValue, T newValue);}
由于这不是功能接口,因此不能使用lambda表达式创建实现。 相反,您可以在需要时创建一个匿名类:
创建一个匿名实现
ValueListener<String> anonymousListener = new ValueListener<String>() {@Overridepublic void invalidated(String formerValue) {valueInvalidated(formerValue);}@Overridepublic void changed(String formerValue, String newValue) {valueChanged(formerValue, newValue);}
};
相反,我们可以一次创建接口的功能实现:
功能实现
public class FunctionalValueListener<T> implements ValueListener<T> {private final Consumer<T> invalidated;private final BiConsumer<T, T> changed;public FunctionalValueListener(Consumer<T> invalidated,BiConsumer<T, T> changed) {this.invalidated = invalidated;this.changed = changed;}@Overridepublic void invalidated(T formerValue) {invalidated.accept(formerValue);}@Overridepublic void changed(T formerValue, T newValue) {changed.accept(formerValue, newValue);}}
此类的实例可以更简洁地创建,并且不会造成混淆:
实例化功能实现
ValueListener<String> functionalListener = new FunctionalValueListener<>(this::valueInvalidated,this::valueChanged);
另一个例子
实际上触发这个想法的是我在代码库中看到的许多Swing的AbstractAction
匿名实现:
Action action = new AbstractAction() {@Overridepublic void actionPerformed(ActionEvent e) {performedAction(e);}
};
这尖叫着“ LAMBDA EXPRESSION!” 但是您不能在抽象类上使用它。 但是在创建只需要使用Consumer<ActionEvent>
的功能实现之后,您可以这样做,它看起来像这样:
Action action = new FunctionalAction(this::performedAction);
好多了吧?
跟进
我将尝试几个星期,并报告其工作原理。 我已经看到了一些问题(JDK提供的功能接口的多样性和异常)以及至少一种改善此模式的方法。
但是我认为值得讨论这种方法。 我也这么认为,为什么不分享呢?
您也会尝试吗? 想到更多问题还是需要改进? 也许您只是觉得这很愚蠢? 无论如何,无论您在哪里找到我,都请发表评论,写帖子或ping我。
反射
我表示不喜欢匿名类的冗长和混淆。 长的那些永远不应该放在首位(使它们嵌套类或自己的类),但是短的有时是最佳选择。
通过短接口或抽象类的功能实现,我们可以改用lambda表达式,方法引用或构造函数引用,并从它们的简洁性和可读性中受益。
翻译自: https://www.javacodegeeks.com/2015/04/getting-rid-of-anonymous-classes.html