转载

A Kotlin Wishlist for Java ( 给Java开发者的Kotlin愿望清单) 中英文对照

毫无疑问,Java在编程语言方面享有优越的地位,被认为是最重要的开发语言之一。 但是,在JVM之上开发了许多语言,比如   Kotlin

Kotlin是一种用于现代多平台应用程序的静态类型编程语言。 虽然我在很长一段时间内都是Java开发人员,但是在项目 数据匿名化方面的工作 让我开始考虑使用Kotlin。 

这些是Kotlin的一些功能,我希望看到它在Java中占有一席之地。

Promote Immutability

Java 9 promotes immutability by introducing factory methods to create collections. It would be great to see immutable collections embedded in the language, rather than relying on wrappers to generate immutable collections. existingDepartments() is a function that returns an immutable list of Strings in Kotlin.

Java 9通过引入工厂方法来创建集合来提升不变性。 很高兴看到嵌入在语言中的不可变集合,而不是依赖包装器来生成不可变集合。 函数  existingDepartments()   是一个在Kotlin中返回不可变字符串列表的函数。

//Kotlin
fun existingDepartments(): List<String> = 
   listOf("Human Resources", "Learning & Development", "Research")

Java 9 comes closest to returning an immutable list by throwing an UnsupportedOperationException when an attempt is made to add or remove an element from the list. It would be great to have a separate hierarchy of mutable and immutable collections and avoid exposing add/remove or any other mutating methods from immutable collections.

当尝试从列表中添加或删除元素时,Java 9最接近返回不可变列表,方法是抛出UnsupportedOperationException。 拥有可变和不可变集合的单独层次结构并避免从不可变集合中暴露添加/删除或任何其他变异方法将是很棒的。

//pre Java 8
public List<String> existingDepartments() {
   return new ArrayList<String>(){{
            add("Human Resources");
            add("Learning & Development");
            add("Research");
        }};
}
//Java 8
public List<String> existingDepartments() {
   return Stream.of("Human Resources", "Learning & Development", "Research")
               .collect(Collectors.toList());
}
//Java 9
public List<String> existingDepartments() {
   return List.of("Human Resources", "Learning & Development", "Research");
}

Being more explicit about immutable collections and letting immutable collections speak out loud for themselves should be given preference over exposing methods and throwing UnsupportedOperationExceptions

Method Parameters Should Be Final by Default

With an intent to promote immutability and avoid errors because of mutation, it might be worth to at least giving a thought to making method parameters final by default.

//Kotlin
fun add (augend: Int, addend: Int) = augend + addend

Parameters for the add()  function are val by default cannot be changed, which means as a client of any function, I can rest assured that the function is not changing the arguments ( not to be confused with object mutation ) that are passed to it.

Making method parameters final by default might and will most likely break existing code bases on Java upgrades but is worth giving a thought

Handle NULL at Compile Time

Every Java developer is bound to know the infamous NullPointerException. Kotlin took a major step by handling NULLs at compile time. Everything is non-null be default until it is explicitly stated.

Did Java 8 not introduce Optional for the very same reason ? Let's see with an example:

每个Java开发人员都必须知道臭名昭着的NullPointerException。 Kotlin通过在编译时处理NULL来迈出了重要的一步。 在明确说明之前,一切都是非空的。

Java 8是否因为同样的原因没有引入Optional? 让我们看一个例子:

//Kotlin
class Employee(private val id: Int, private val department: Department?) {
  fun departmentName() = department?.name ?: "Unassigned"
}
class Department(val name: String)
/**
    Employee needs a non-nullable "id" and an optional department to be constructed.
    val employee = Employee(null, null) => <b> Compile Time Error </b>
**/

The  Employee class has a primary constructor with a  non-nullable id and an optional (nullable) department . Passing null for the id will result in a compile time error.

The departmentName()  function accesses the name property of Department using the optional operator  ? on the nullable field. If department is null, name will not be accessed and the expression on the left-hand side  [department?.name] will return null. The  Elvis operator  ?: will return the right hand side ( "Unassigned" ) if the left-hand side of the expression is null.

//Java 8
class Employee {
    private Integer id;
    private Optional<Department> department
    Employee(Integer id, Optional<Department> department){
        this.id = id;
        this.department = department;
    }
    public String departmentName() {
        return department.orElse("Unassigned");
    }
}
/**
    Employee needs a non-nullable "id" and an optional department to be constructed.
    Employee employee = new Employee(null, null); <b>NPE !!!</b>
**/

Optional will not protect the code from NPE, but Optional has its advantages:

  • It makes the domain model clear. The Employee class has an optional department, which is good enough to conclude that every employee may not be assigned a department

  • It promotes composability as in the departmentName() method

Handling NULLs at compile time should result in cleaner code by removing unnecessary NULL checks in the form of an if statement, Objects.requireNonNull, Preconditions.checkNotNull, any other form

To keep things simple, department was passed in to the constructor even though this is an optional attribute.

Optional不会保护代码免受NPE的影响,但Optional有其优点:

  • 它使域模型清晰。 该   员工 类有一个可选的部门,这是不够好,得出结论,每一个员工可能无法指派一个部门

  • 它像 departmentName()   方法 一样促进可组合性 

在编译时处理NULL应该通过删除if语句,Objects.requireNonNull,Preconditions.checkNotNull,任何其他形式的形式的不必要的NULL检查来产生更清晰的代码

为了简单起见,即使这是一个可选属性,部门也会被传递给构造函数。

Improve Lambdas

Java 8 introduced lambdas, which are built on top of a functional interface and a functional descriptor, meaning every lambda expression will map to the signature of an abstract method defined in that functional interface. This effectively means it is a mandate to have an interface (Functional Interface) with only one abstract method (Functional Descriptor) to create a lambda expression.

Java 8引入了lambda,它建立在功能接口和功能描述符之上,这意味着每个lambda表达式都将映射到该功能接口中定义的抽象方法的签名。 这实际上意味着要求一个接口(Functional Interface)只有一个抽象方法(Functional Descriptor)来创建一个lambda表达式。 

//Kotlin
val isPositive: (Int) -> Boolean = { it > 0 }
OR,
val isPositive: (Int) -> Boolean = { num > 0 }
OR,
val isPositive: (Int) -> Boolean = { num: Int > 0 }
//Usage
isPositive(10) returns true
isPositive(-1) returns false

Above, the variable  isPositive  is  a function that takes an Int as an argument and returns a Boolean. The value of this variable is a function definition or a lambda defined in braces, which checks that the passed argument is greater than zero.

Whereas, as seen in Java below, Predicate is a functional interface containing an abstract method  test() — which takes an argument of type T and returns a boolean.

So, isPositive takes an argument of type Integer and checks that it is greater than zero. In order to use it, we need to invoke the  test() method on  isPositive .

//Java 8
private Predicate<Integer> isPositive = (Integer arg) -> arg > 0;
//Usage
isPositive.test(10) returns true
isPositive.test(-1) returns false
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

Lambdas should be independent of functional interfaces and their functional descriptors.

Lambdas应该独立于功能接口及其功能描述符.

Support Extension Functions

Kotlin supports extension functions, which provide the ability to extend a class with new functionality without having to inherit from the class or use any type of design pattern, such as Decorator.

Let's write an extension function to return the last character of a String, meaning "Kotin".lastChar() will return 'n'.

//Kotlin
fun String.lastChar() = this.toCharArray()[this.length - 1]
/**
    Extension functions are of the form -
    fun <ReceiverObject>.function_name() = body
    OR,
    fun <ReceiverObject>.function_name(arg1: Type1, ... argN: TypeN) = body
**/

Here, lastChar() is an extension function defined on String, which is called a receiver object. This function can now be invoked as  "Kotlin".lastChar().

Extension functions provide an ability to extend a class with new functionalities without inheritance or any other design pattern

Tail Recursion

Kotlin provides support for Tail-recursion. Tail-recursion is a form of recursion in which the recursive calls are the last instructions in the function (tail). In this way, we don't care about previous values, and one stack frame suffices for all of the recursive calls; tail-recursion is one way of optimizing recursive algorithms.

The other advantage/optimization is that there is an easy way to transform a tail-recursive algorithm to an equivalent one that uses iteration instead of recursion.

Kotlin为 Tail-recursion 提供支持 尾递归是一种递归形式,其中递归调用是函数(尾部)中的最后一条指令。 通过这种方式,我们不关心以前的值,并且一个堆栈帧足以满足所有递归调用;  尾递归是优化递归算法的一种方法。

另一个优点/优化是有一种简单的方法可以将尾递归算法转换为使用迭代而不是递归的等效算法。 

//Kotlin

fun factorialTco(val: Int): Int {
    tailrec fun factorial(n: Int, acc: Int): Int = 
         if ( n == 1 ) acc else factorial(n - 1, acc * n)
  return  factorial(val, acc = 1)
}

When a function is marked with the tailrec modifier and meets the required form, the compiler optimizes out the recursion, leaving behind a fast and efficient loop-based version instead.

Effectively, a tail-recursive function can execute in constant stack space, so it's really just another formulation of an iterative process.

实际上,尾递归函数可以在常量堆栈空间中执行,因此它实际上只是迭代过程的另一种表达 .

Java does not directly support tail-call optimization at the compiler level, but one can use lambda expressions to implement it. It would be nice to see TCO at the compiler level.

Java不直接支持编译器级别的尾调用优化,但可以使用 lambda表达式 来实现它。 在编译器级别看到TCO会很高兴。

Miscellaneous

  • Remove inherent duplication [new, return, semicolon]: Kotlin does not require  new to create an instance. It still needs a  return  if a function is treated as a statement instead of an expression.

删除固有的重复[new,return,semicolon]:   Kotlin不需要 new   来创建实例。 如果将函数视为语句而不是表达式, 它仍然需要 返回

//Kotlin
class Employee(private val id: Int, private val department: Department?) {
    //no return
    fun departmentNameWithoutReturn() = department?.name ?: "Unassigned"
    //return is needed if a function is treated as a statmentrather than an expression
    fun departmentNameWithoutReturn() {
        val departmentName = department?.name ?: "Unassigned"
        return departmentName
    }
}
  • Singleton Classes: It would be great to see an easier way to create singleton classes in Java. An equivalent syntax in Kotlin is seen below.

//Kotlin
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
        // ...
    }
}
  • Immutable Classes: It would be good to see something like the  readonly / immutable  modifier to create an immutable class. The below mentioned code snippet is simply a thought (not available in Kotlin or Java).

//Hypothetical [Not available so far]
immutable class User(private val name: String, private val id: Int)

In conclusion, as developers, we will always make mistakes (skipping NULL checks, mutating a collection, etc.), but providing such features at the language level will make our lives easier and prevent mistakes.

原文  http://mp.weixin.qq.com/s?__biz=MzA5OTI2MTE3NA==&mid=2658337582&idx=1&sn=7b38cdb0befcaa2be170cb48fb6abf31
正文到此结束
Loading...