前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >kotlin和java语言_我希望Java可以从Kotlin语言中窃取的10个功能

kotlin和java语言_我希望Java可以从Kotlin语言中窃取的10个功能

作者头像
用户7886150
修改2020-12-14 15:15:47
1.1K0
修改2020-12-14 15:15:47
举报
文章被收录于专栏:bit哲学院bit哲学院

参考链接: 有关Java中数组分配的有趣事实

kotlin和java语言

  本文已过期。 在围绕Kotlin 1.0的发行大肆宣传之后,让我们认真看一下我们也应该在Java中拥有的一些Kotlin语言功能。  

  在本文中,我不会希望有独角兽。 但是有一些悬而未决的成果(据我天真地看到),可以将它们引入Java语言而不会带来很大的风险。 在阅读本文时,请确保将粘贴示例复制到http://try.kotlinlang.org (Kotlin的在线REPL)  

 1.数据类别 

  语言设计师几乎从未同意类是什么的必要性和功能范围。 奇怪的是,在Java中,每个类始终具有标识这个概念,而在现实世界中所有Java类的80%到90%都不需要这个概念。 同样, Java类始终具有可在其上进行同步的监视器 。  

  在大多数情况下,编写类时,您实际上只是想对值进行分组,例如字符串,整数,双精度型。 例如:  

 public class Person {

    final String firstName;

    final String lastName;

    public JavaPerson(...) {

        ...

    }

    // Getters

    ...

    // Hashcode / equals

    ...

    // Tostring

    ...

    // Egh...

}

  当您完成上述所有操作时,手指将不再用力。 Java开发人员针对上述情况实施了丑陋的解决方法,例如IDE代码生成或lombok ,这是所有黑客中最大的。 在一个更好的Java中,Lombok中实际上不需要任何东西。  

  例如,如果Java具有Kotlin的数据类 :  

 data class Person(

  val firstName: String,

  val lastName: String

)

  以上就是声明与前面的Java代码等效的全部内容。 因为数据类用于存储数据(duh)(即值),所以hashCode() , equals() , toString()很明显,并且可以默认提供。 此外,数据类是一等元组,因此它们可以照此使用,例如在各个引用中再次对其进行分解:  

 val jon = Person("Jon", "Doe") 

val (firstName, lastName) = jon

  在这种情况下,我们可能希望。 正在设计Valhalla / Java 10及其值类型 。 我们将看到直接在JVM和Java语言上提供多少功能。 这无疑将是一个令人兴奋的补充。  

  请注意,在Kotlin中val是如何可能的: 局部变量类型推断。 现在正在为将来的Java版本进行讨论 。  

 2.默认参数 

  您会重载API多少次,如下所示:  

 interface Stream<T> {

    Stream<T> sorted();

    Stream<T> sorted(Comparator<? super T> comparator);

}

  上面是完全相同的JDK Stream操作。 第一个简单地将Comparator.naturalOrder()应用于第二个。 因此,我们可以在Kotlin中编写以下代码 :  

 fun sorted(comparator : Comparator<T> 

         = Comparator.naturalOrder()) : Stream<T>

  当只有一个默认参数时,这种优势不会立即显现出来。 但是想象一下一个带有大量可选参数的函数:  

 fun reformat(str: String,

             normalizeCase: Boolean = true,

             upperCaseFirstLetter: Boolean = true,

             divideByCamelHumps: Boolean = false,

             wordSeparator: Char = ' ') {

...

}

  可以通过以下任何一种方式调用它:  

 reformat(str)

reformat(str, true, true, false, '_')

reformat(str,

  normalizeCase = true,

  upperCaseFirstLetter = true,

  divideByCamelHumps = false,

  wordSeparator = '_'

)

  默认参数的功能在于,当按名称而不是按索引传递参数时,它们特别有用。 JVM当前不支持此功能,直到Java 8才完全不保留参数名称( 在Java 8中,您可以为此打开JVM标志 ,但是使用Java的所有传统,则不应依赖在此呢)。  

  哎呀,此功能是我每天在PL / SQL中使用的功能。 当然, 在Java中,您可以通过传递参数object来解决此限制 。  

 3.简化的检查实例 

  如果您愿意,这实际上是switch的instanceof。 某些人可能会声称这些东西是邪恶的,糟糕的OO设计。 Nja nja。 我说,这种情况时有发生。 显然,在Java 7中,字符串开关被认为足够通用以修改语言以允许它们。 为什么不使用instanceof开关?  

 val hasPrefix = when(x) {

  is String -> x.startsWith("prefix")

  else -> false

}

  这不仅可以执行instanceof开关,而且还以可分配表达式的形式进行。 when表达式功能强大when Kotlin对此when 。 您可以混合使用任何种类的谓词表达式,类似于SQL的CASE表达式。 例如,这也是可能的:  

 when (x) {

  in 1..10 -> print("x is in the range")

  in validNumbers -> print("x is valid")

  !in 10..20 -> print("x is outside the range")

  else -> print("none of the above")

}

  与SQL比较(并非在所有方言中都实现):  

 CASE x

  WHEN BETWEEN 1 AND 10 THEN 'x is in the range'

  WHEN IN (SELECT * FROM validNumbers) THEN 'x is valid'

  WHEN NOT BETWEEN 10 AND 20 'x is outside the range'

  ELSE 'none of the above'

END

  如您所见,只有SQL比Kotlin更强大。  

 4.映射键/值遍历 

  现在,仅使用语法糖就可以非常轻松地完成此操作。 当然,具有局部变量类型推断将是一个加号,但请检查一下  

 val map: Map<String, Int> = ...

  现在,您可以执行以下操作:  

 for ((k, v) in map) {

    ...

}

  毕竟,在大多数情况下,遍历地图都是通过Map.entrySet() 。 Map可能已经增强,可以在Java 5中扩展Iterable<Entry<K, V>> ,但没有。 真可惜。 毕竟,它已在Java 8中得到增强,以允许通过Map.forEach()对Java 8中的条目集进行内部迭代:  

 map.forEach((k, v) -> {

    ...

});

  JDK上帝,还不算太晚。 您仍然可以让Map<K, V> extend Iterable<Entry<K, V>>  

 5.地图访问文字 

  这一功能将为Java语言增加无数的价值。 像大多数其他语言一样,我们有数组。 与大多数其他语言一样,我们可以使用方括号访问数组元素:  

 int[] array = { 1, 2, 3 };

int value = array[0];

  还要注意一个事实,我们在Java中拥有数组初始化文字,这很棒。 那么,为什么不同时允许使用相同的语法访问地图元素呢?  

 val map = hashMapOf<String, Int>()

map.put("a", 1)

println(map["a"])

  实际上, x[y]只是x.get(y)支持的方法调用的语法糖。 太好了,我们立即将Record.getValue()方法重命名为Record.get() (当然,将旧方法保留为同义词),这样您现在就可以像这样取消引用数据库记录值了。 ,位于Kotlin  

 ctx.select(a.FIRST_NAME, a.LAST_NAME, b.TITLE)

   .from(a)

   .join(b).on(a.ID.eq(b.AUTHOR_ID))

   .orderBy(1, 2, 3)

   .forEach {

       println("""${it[b.TITLE]} 

               by ${it[a.FIRST_NAME]} ${it[a.LAST_NAME]}""")

   }

  由于jOOQ将所有列类型信息保存在单个记录列上,因此您实际上可以预先知道it[b.TITLE]是String表达式。 很好,是吗? 因此,此语法不仅可以与JDK映射一起使用,而且可以与公开基本get()和set()方法的任何库一起使用。  

  请继续关注此处的更多jOOQ和Kotlin示例: https : //github.com/jOOQ/jOOQ/blob/master/jOOQ-examples/jOOQ-kotlin example / src / main / kotlin / org / jooq / example / kotlin / FunWithKotlinAndJOOQ .kt  

 6.扩展功能 

  这是一个有争议的话题,当语言设计师对此一无所知时,我可以完全理解。 但是时不时地, 扩展功能非常有用。 实际上,这里的Kotlin语法只是为了让函数假装为接收器类型的一部分:  

 fun MutableList<Int>.swap(index1: Int, index2: Int) {

  val tmp = this[index1] // 'this' corresponds to the list

  this[index1] = this[index2]

  this[index2] = tmp

}

  现在,这将允许交换列表中的元素:  

 val l = mutableListOf(1, 2, 3)

l.swap(0, 2)

  这对于jOOλ之类的库将非常有用,该库通过将Java 8 Stream API封装为jOOλ类型来扩展Java 8 Stream API( 另一个此类库是StreamEx ,重点稍有不同)。 该jOOλ Seq包装类型是不是真的很重要,因为它伪装成一个Stream的类固醇。 如果可以通过导入将jOOλ方法人工地应用于Stream上,那就太好了:  

 list.stream()

    .zipWithIndex()

    .forEach(System.out::println);

  zipWithIndex()方法并不真正存在。 上面的代码只会翻译成以下不太易读的代码:  

 seq(list.stream())

    .zipWithIndex()

    .forEach(System.out::println);

  实际上,扩展方法甚至允许绕过将所有内容显式地包装在stream() 。 例如,您可以这样做:  

 list.zipWithIndex()

    .forEach(System.out::println);

  由于所有jOOλ的方法都可以设计为也可应用于Iterable 。  

  同样,这是一个有争议的话题。 例如,因为  

   @rafaelcodes面向对象的优点是什么? 现在,我们编写了receive.send(message),而不是send(receiver,message)。  

   — Lukas Eder(@lukaseder) 2016年1月28日  

  虽然给人一种虚拟的假象,但扩展功能实际上只是加糖的静态方法。 进行这种欺骗对于面向对象的应用程序设计是一个巨大的风险,这就是为什么此功能可能不会将其纳入Java的原因。  

 7.安全呼叫接线员(以及:猫王接线员) 

  可选的是meh。 可以理解,需要引入一个Optional类型,以便在缺少基本类型值(不能为null)的情况下进行抽象。 现在,我们有了OptionalInt东西,例如,为以下事物建模:  

 OptionalInt result =

IntStream.of(1, 2, 3)

         .filter(i -> i > 3)

         .findFirst();

// Agressive programming ahead

result.orElse(OR_ELSE);

  可选的是单子  

   Google似乎对Monad是什么也有些困惑…… pic.twitter.com/eJp9jY9cwG  

   — Mario Fusco(@mariofusco) 2013年10月13日  

  是。 它允许您将flatMap()的值缺失。  

  当然,如果您想进行复杂的函数式编程,则将开始在各处键入map()和flatMap() 。 像今天一样,当我们键入getter和setter时。 随之而来的是lombok生成平面映射调用,而Spring将添加一些@AliasFor样式标注以进行平面映射。 只有开明的人才能解密您的代码。  

  在回到日常业务之前,我们只需要一个简单的空安全操作员即可。 喜欢:  

 String name = bob?.department?.head?.name

  我真的很喜欢Kotlin中的这种实用主义。 还是您更喜欢(平面)映射?  

 Optional<String> name = bob

    .flatMap(Person::getDepartment)

    .map(Department::getHead)

    .flatMap(Person::getName);

  你能读懂这个吗? 我不能。 我也不能写这个。 如果您弄错了,您将被Boxoxed。  

   “ @EmrgencyKittens :盒子里的猫,盒子里的猫。 pic.twitter.com/ta976gqiQs ”而且我认为flatMap  

   — Channing Walton(@channingwalton) 2014年3月23日  

  当然, 锡兰语是唯一使null正确的语言 。 但是Ceylon具有Java 42之前无法提供的大量功能,我也不希望有独角兽。 我希望有安全调用运算符(还有Elvis运算符,两者稍有不同),也可以用Java实现。 上面的表达式只是用于以下目的的语法糖:  

 String name = null;

if (bob != null) {

    Department d = bob.department

    if (d != null) {

        Person h = d.head;

        if (h != null)

            name = h.name;

    }

}

  这种简化可能有什么问题?  

 8.一切都是一种表达 

  现在,这可能只是一个独角兽。 我不知道是否存在JLS /解析器限制,这将永远使我们陷入语句和表达式之间史前区分的痛苦之中。  

  在某个时间点上,人们开始对产生副作用的事物使用语句,而对更具功能性的事物使用表达式。 因此,毫不奇怪,所有的String方法都是真正的表达式,对不可变的字符串进行操作,并始终返回新的字符串。  

  例如,这似乎与Java中的if-else不合适,后者可能包含块和语句,而每个块和语句都可能产生副作用。  

  但这真的是必要条件吗? 我们也不能用Java编写类似的东西吗?  

 val max = if (a > b) a else b

  好的,我们使用?:有这个奇怪的条件表达式。 但是Kotlin的when (即Java的switch )呢?  

 val hasPrefix = when(x) {

  is String -> x.startsWith("prefix")

  else -> false

}

  难道不是比以下等效的工具有用吗?  

 boolean hasPrefix;

if (x instanceof String)

    hasPrefix = x.startsWith("prefix");

else

    hasPrefix = false;

  (是的,我知道?: 。我只是觉得if-else更容易阅读,而且我不明白为什么那应该是一个陈述,而不是一个表达。Heck,在Kotlin中,甚至try是一个表达,而不是一个陈述。 :  

 val result = try {

    count()

} catch (e: ArithmeticException) {

    throw IllegalStateException(e)

}

  美丽!  

 9.单表达函数 

  现在这个。 这将节省大量的时间来阅读和编写简单的粘合代码。 实际上,我们已经在批注中包含了语法。 例如,查看Spring神奇的@AliasFor批注。 它产生:  

 public @interface AliasFor {

    @AliasFor("attribute")

    String value() default "";

    @AliasFor("value")

    String attribute() default "";

}

  现在,如果您真的很quin眼,这些只是产生常数值的方法,因为注释只是其实现使用生成的字节码的接口。 我们可以讨论语法。 当然, default这种不规则用法很奇怪,因为默认情况下Java 8中没有重复使用它,但是我想Java总是需要额外的语法,以便开发人员可以更好地感觉自己的打字手指,使他们活着。 没关系。 我们可以忍受。 但是话又说回来,为什么我们必须这样做? 为什么不仅仅收敛于以下内容?  

 public @interface AliasFor {

    String value() = "";

    String attribute() = "";

}

  和类/接口默认方法也一样吗?  

 // Stop pretending this isn't an interface

public interface AliasFor {

    String value() = "";

    String attribute() = "";

}

  现在才好看。 但是鉴于Java现有的语法,这可能只是一个独角兽,所以让我们继续...  

 10.流量敏感型 

  现在这个 。 这个!  

  我们之前已经在博客中介绍了总和类型。 自Java 7以来,Java的总和类型有所例外:  

 try {

    ...

}

catch (IOException | SQLException e) {

    // e can be of type IOException and/or SQLException

    // within this scope

}

  但是不幸的是,Java没有流敏感类型。 流敏感类型在支持求和类型的语言中至关重要,但在其他方面也很有用。 例如,在Kotlin中:  

 when (x) {

    is String -> println(x.length)

}

  显然,我们不需要强制转换,因为我们已经检查了x is String 。 相反,在Java中:  

 if (x instanceof String)

    System.out.println(((String) x).length());

  Aaagh,所有这些输入。 IDE自动补全功能非常聪明,足以提供上下文类型的方法,然后为您生成不必要的强制转换。 但是,如果永远不需要这样做,那就很好了,每次我们使用控制流结构显式缩小类型时,它就很棒。  

  有关更多信息,请参阅有关流量敏感类型的Wikipedia条目 。 可以绝对添加到Java语言中的功能。 毕竟,自Java 8以来,我们已经获得了对流量敏感的最终局部变量。  

 11.(奖金)声明地点差异 

  最后但并非最不重要的一点是,通过声明网站的variance获得更好的泛型 。 许多其他语言也知道这一点,例如C#的IEnumerable :  

  公共接口IEnumerable <out T>:IEnumerable  

  这里的关键字out表示通用类型T是由IEnumerable类型产生的(与in相对,它代表消费)。 在C#,Scala,Ceylon,Kotlin和许多其他语言中,我们可以在类型声明中声明它,而不是在其用法上声明(尽管许多语言都允许这两种)。 在这种情况下,我们说IEnumerable与它的类型T是协变的,这再次意味着IEnumerable<Integer>是IEnumerable<Object>的子类型。  

  在Java中,这是不可能的,这就是为什么Java新手在Stack Overflow上有一个不计其数的问题 。 我为什么不能...  

 Iterable<String> strings = Arrays.asList("abc");

Iterable<Object> objects = strings; // boom

  在像Kotlin这样的语言中,以上是可能的。 毕竟,为什么不呢? 可以产生字符串的事物也可以产生对象,我们甚至可以在Java中以这种方式使用它:  

 Iterable<String> strings = Arrays.asList("abc");

for (Object o : strings) {

    // Works!

}

  缺少声明站点差异已使许多API变得非常易懂。 考虑Stream :  

 <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

  这只是噪音。 从本质上说,一个函数与其参数类型是互变的,而其结果类型是协变的,那么对Function或Stream的更好定义是:  

 interface Function<in T, out R> {}

interface Stream<out T> {}

  如果可能的话,那是全部? super ? super和? extends ? extends垃圾可以删除而不会丢失任何功能。  

  如果您想知道我在说什么?  

   解释了协方差和自变量。 资料来源: https: //t.co/2S4ChNeAvq pic.twitter.com/BfOME8puj2  

   — Lukas Eder(@lukaseder) 2016年1月12日  

  好消息是,正在为Java的(近)未来版本进行讨论: http : //openjdk.java.net/jeps/8043488  

 结论 

  Kotlin是一种很有前途的语言,即使对于似乎已经决定的游戏来说太晚了,也不赞成在JVM上使用其他语言。 但是,这是一种非常有趣的语言,可以学习,并且可以对一些简单的事情做出很多非常好的决定。  

  这些决定中的一些希望有望被Java语言之神采纳并整合到Java中。 此列表显示了一些可能“容易”添加的功能。  

   @BrianGoetz @lukaseder设计一种语言有多困难? 这只是您放入解析器生成器中的语法! #getthepopcorn  

   — AlekseyShipilëv(@shipilev) 2016年3月11日  

  有关Kotlin习语的更多信息: https : //kotlinlang.org/docs/reference/idioms.html  

  翻译自: https://www.javacodegeeks.com/2016/04/10-features-wish-java-steal-kotlin-language.html

 kotlin和java语言

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档