Java现代化-语言功能的愿望清单(第2部分)

In this second part of the "Modernizing Java" series, we will consider language features from C#, JavaScript (ES6) and others. The first part of the series can be found here.

Features from Ruby

Ruby是一种脚本语言,特别以Web框架“ ruby​​ on rails”而闻名。 就脚本语言而言,这是一种非常干净的语言,它是我遇到的第一种语言。让关键字和协程的概念。 基本上,让允许您退出函数的当前控制流,当下次调用该函数时,您从上次中断的地方继续:

// this is how coroutines could look in Java
public Iterator<Number> powersOfTwo(){
   int current = 1;
   while(true){
      yield current;  // note the new "yield" keyword here
      current *= 2;
   }
}

The example above is a generator for an infinite sequence. Note that we do not burn CPU cycles with our while(true) loop here. Since we exit the control flow in each iteration, only one iteration is executed for each call to ìterator.next(). The returned iterator is implicit, you don't need to define it. This concept has also been adapted by ES6, Python, C# and many other languages, and people are putting it to great use (hello, Redux Saga!). Like many other features in this blog series, this is a quality-of-live enhancement and can be "emulated" in standard Java. However, I really think that this would be very useful.

Features from C#

程序员经常将C#标记为“ Java的下一个发展”。 确实,这两种语言具有许多共同点,并且如果不是由于标准库中的差异,则很可能会编写一个将C#源代码文件转换为Java源代码文件的反编译器,反之亦然。 全面的讨论超出了本文的范围。 C#提供了Java中不存在的许多有趣的语言功能。

Partial classes

在C#中,您可以将一个类标记为部分的。 这使您可以将一个类拆分为多个文件,但是编译器将它们视为一个:

// in file "myClassPart1.cs"
public partial class MyClass {

}

// in file "myClassPart2.cs"
public partial class MyClass {

}

It's different from an import statement, because in the end, there is only one class in the binary files. "Why would somebody want to do that?" you may ask. The primary reason why this useful is code generation. For example, there are powerful WYSIWIG UI builders that produce C# source code files (e.g. one is integrated in VisualStudio). If you ever had the questionable pleasure of dealing with code generation then you will know the pain of having to manually edit auto-generated files. The problem is: once you re-run the generator, your manual changes are lost. In the Java world, there have been efforts to "mark" sections of the hand-written code as such, so that the generator will leave them alone (see, for example, the code generation facilities of EMF). With partial classes, those pains are gone for good. The generator controls one file (one part of the class) while your hand-written code goes into an entirely different file, which just happens to be another part of the very same class. You can be sure that your hand-written changes will not be overwritten by some automated generator, because they reside in a different file of which the generator is unaware. This is a feature that only concerns the Java compiler, the runtime remains untouched because in the end, only a single *.class file is produced. Java is a popular target for code generation, and having partial classes would help to ease the pain with generated code a lot.

The event keyword

这是C#的相对较小的细节,但我个人很喜欢:事件关键词。 您多久用Java编写一次这样的代码:

private Set<EventListener> eventListeners= new HashSet<>();

public void registerEventListener(EventListener listener){
   this.eventListeners.add(listener);
}

public void removeEventListener(EventListener listener){
   this.eventListeners.remove(listener);
}

public void fireEvent(Event event){
   for(Listener listener : this.eventListeners){
      listener.onEvent(event);
   }
}

这真的是重复的。 如果您有一个处理5个不同事件类的类,则上面的代码必须重复并修改四次。 在C#中,您将获得上述所有代码,如下所示:

public event MyEvent MyEvent;

如果要添加事件侦听器:

myClass.MyEvent += myListener;

...并在内部触发该事件:

this.MyEvent(event);

看妈,不行! 这是一件很小的事情,但是它消除了许多样板代码。 一般而言,是否使用观察者模式是一个好主意,这是完全不同的讨论。

Tuples

在最新版本的C#中,添加了对元组的本机支持。 这使您可以轻松地构造,传递和解构对,三元组,四元组(您命名)。 看起来是这样的:

(int count, double sum, double sumOfSquares) = ComputeSumAndSumOfSquares(sequence);

这里发生了什么?ComputeSumAndSumOfSquares返回一个三元组,其中包含计数,总和和平方和。 我们通过一个方法调用接收所有三个值。 如果我们对这三个都不感兴趣,可以将变量声明替换为_:

(_, double sum, _) = ComputeSumAndSumOfSquares(sequence);

很简单,很优雅,很可惜它在Java中不存在。

nameof

良好的编程习惯是编写前提条件,以确保所接收的参数确实与规范匹配。 这使您的方法快速失败并提供精确的错误消息。 现在,如果您考虑以下代码:

public long sum(Iterator<Long> values){
   if(values == null) { throw new IllegalArgumentException("Argument 'values' must not be NULL!"}
   // ...
}

...您会注意到价值观出现两次:一次作为参数名称,一次在字符串文字内。 本身就很好,但是如果我改名变量? 字符串文字不会改变,因为IDE并未意识到两者之间的语义相关性(您也可以在字符串内部启用替换,但这还有其他问题……)。 C#提供了一个优雅的解决方案:

public long Sum(IEnumerator<Long> values){
   if(values == null) { throw new ArgumentException("Argument '" + nameof(values) + "' must not be NULL!"}
   // ...
}

如你看到的,的名字无需将变量名硬编码为字符串文字。的名字产生名称传递的变量作为字符串。 另一个小东西,但很有用,尤其是对于错误消息。

Features from JavaScript (in particular ES6)

ES6在语法方面对JavaScript进行了两个非常巧妙的增强。

Object Deconstruction

最有用的一种叫做对象解构。 您多久用Java编写一次这样的代码:

MethodResult result = someMethod();
int size = result.size();
byte[] data = result.getData();
User author = result.getAuthor();

ES6在这里消除了很多仪式:

const { size, data, author } = someMethod();

这类似于C#元组,但不完全相同。 ES6在以下对象的结果对象中寻找同名字段someMethod,并将它们分配给新的局部变量。 解构函数实际上可以做更多的事情(例如,在不存在的情况下重命名和分配默认值),但这是另一篇博客文章。 尽管这在Java中无法顺利运行(因为需要标识和调用getter等),但是按照这些原则进行定义确实很有用。

Implicit conversion from Object to Boolean

在编写JavaScript代码时,正如我通常讨厌隐式转换一样,我喜欢使用一种构造:

if(this.header){
   // render header
}

注意标头在上面的代码中,不是布尔值,而是数据结构。 通过在如果 statement, we check 如果 it is 空值(要么未定义, but that's another story). This implicit conversion from Object to boolean by checking 空值-ness is definitly useful. However, it does have some issues in JavaScript when it comes to working with numeric values, because the number 0也隐式转换为假; a convention that should never have reached beyond low-level languages like C in my opinion. Checking for 空值-ness is a very common task in Java, and making it quicker and easier to do seems like a good idea.

From C/C++

您是否曾经在Java中遇到过要编写可配置大小(以兆字节为单位)的缓存的情况? 好吧,那你就陷入了大麻烦。 在Java中,您不知道对象有多大其实是。 通常,您无需在意,但是如果遇到这种情况,这些问题将复仇。 您可以估计通过反射来确定对象的大小,但这是一个缓慢且昂贵的操作。 另外,您可以通过代理使用Java工具,但是这会使应用程序的部署变得复杂,并且总体上感觉错误,因为您只想做一些简单的事情,例如测量内存中对象的大小。 我真正想在Java中看到的是C / C ++提供的即用型功能,即大小关键词。 我意识到这在JVM中不是一件容易的事,但是对于在JVM上编写“客户端”的程序员而言,这几乎是不可能的。

From Haskell

Haskell是一种功能性语言,并且在许多方面都是OCaml的精神继承者。

List comprehension

生成列表是编程中的常见任务。 Haskell通过介绍使这方面变得非常容易清单理解。 例如:

[(i,j) | i <- [1,2], j <- [1..4] ]

...将产生对[(1,1),(1,2,2,(1,3),(1,4),(2,1),(2,2),(2,3),(2,4)]。 尝试使用嵌套的for循环进行操作,您将了解为什么上面的语法很棒。

Partial Application

在Haskell,您可以部分申请功能,在此过程中产生新的功能。 例如:

add x y = x + y
addOne = add 1
add 3 4 -- produces 7
addOne 6 -- also produces 7

addOne现在是一个函数一参数,添加常数1。您今天也可以在Java中执行类似的操作:

BiFunction<Integer, Integer, Integer> add = (a,b) -> a + b;
Function<Integer, Integer> addOne = (a) -> add(1, a);

...除了您需要一个很多更多仪式。 这也类似于捆绑JavaScript函数和默认值参数(有几种语言)。 即使部分应用程序在函数式编程中使用最广泛,它也是一个易于“提取”的方面,因为它不依赖于函数式编程的其他特征(例如惰性求值)。 理论上,它可以使用任何允许函数(或方法或过程或...)调用的语言工作。 我没有任何解释说明为什么很少采用这种简洁功能。

Conclusion

我希望您喜欢这次语言功能之旅。 Java在许多方面都是非常好的语言,但是它需要不断发展。 在这个博客系列中,我试图概述“其他人在做什么”。 我错过了重要的事情吗? 您是否希望在Java中看到其他任何语言功能,而这些功能在本系列中都没有涉及? 在评论中让我知道:)

谢谢阅读!

from: https://dev.to//martinhaeusler/modernizing-java---a-language-feature-wish-list-part-2-7c0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值