为什么Kotlin没有显式输入?

时间:2019-03-05 15:47:18

标签: java android kotlin type-inference

对此我感到好奇,为什么kotlin设计师会认为这是在Kotlin中删除显式键入的好主意?

对我来说,显式键入并不是用Java(或任何其他强类型语言)编写的“痛点”:所有IDE都可以帮助我自动键入变量。

它增加了对代码的理解(这就是为什么我不喜欢弱类型的语言,我不知道我要处理哪种变量的原因)。

此外,这是我的主要问题,它使代码更易于出错。

示例:
Java:很容易识别为字符串,一切都很好

String myPrice = someRepository.getPrice(); // Returns a String
myTextView.setText(myPrice); 

Java:很容易用setText()识别为int,代码气味

int myPrice = someRepository.getPrice(); // Returns an int, code smell !!
myTextView.setText(String.valueOf(myPrice)); // Problem avoided

科特琳:????

val myPrice = someRepository.getPrice(); // What does it return ?
myTextView.setText(myPrice); // Possible hidden bug with setText(@StringRes int) instead of setText(String) !!

在Kotlin imo中,没有显式键入Kotlin是最大的缺点。我试图了解这种设计选择。

我并不是真正地在寻找“补丁”来修复示例/避免出现代码臭味,我试图了解删除显式键入背后的主要原因。它必须不仅仅是“减少键入/更易于阅读”。它只删除了几个字符(一个字符仍然必须写val / var),并且在Kotlin中显式键入仍然会添加一些字符...

在我看来,没有显式输入的静态输入是处理隐藏的bug /意大利面条错误的最坏情况:如果一个类(例如“存储库”)更改了它的返回类型(从String到{{ 1}})。使用显式输入时,编译将在调用“存储库”的类上失败。如果没有显式键入,则编译可能不会失败,并且错误类型的变量可能会“遍历”类,并由于其类型而改变类的行为。这是危险的,未被发现。

修复很容易;明确键入变量。但这就是我们所说的Kotlin,是一种面向代码高尔夫球手的语言:人们不会显式地键入变量,因为在kotlin中这样做比使用turtle-java花费更多的时间。 kes!

2 个答案:

答案 0 :(得分:15)

首先:Kotlin具有静态键入。编译器确切知道哪种类型到达或到达哪里。而且您总是可以将其写下来,例如

autorun

重点是:您可以编写许多代码,而这些代码仅依赖,因为您所做的任何事情都是静态输入的,并且也输入了检查内容!

当您只需要看fun sum(a: Int, b: Int): Int { val c: Int = 1 时,您是否真的需要知道是要添加两个双精度数还是两个整数或两个长整数?

最后,这是关于平衡不同的需求。省略类型 可能会有所帮助:减少人类读者需要阅读和理解的代码。

当然:如果您编写代码使人们不断转向他们的IDE,让IDE告诉他们实际的推断类型,那么您就将有用的功能变成了问题! / p>

这里的真正答案是:取决于情况。我到过很多代码评论中,C ++人员都在讨论a + b关键字的用法。在很多情况下,使用auto确实使代码更易于阅读和理解,因为您可以专注于“变量会发生什么”,而不是花一分钟时间看一下声明,而是试图理解它的类型。但是偶尔也有一些示例,其中auto截然相反,并且“全类型”声明更易于遵循。

OP给出的评论是:首先,您应该知道自己在做什么。含义:编写代码时,您不仅会调用在某个对象上找到的“任何”方法。您必须调用方法。您最好知道它的作用,以及它返回给您的东西。我同意,当有人匆忙时,您迅速进行var-assign,然后将其传递出去,这可能会导致错误。但是对于每种情况,auto都有助于创建错误,可能会发生10个事件,这些事件有助于编写易于阅读的代码。就像说的那样:生活就是平衡。

最后:语言不应无缘无故地添加功能。 Kotlin员工正在仔细权衡他们添加的功能。他们没有添加类型推断,因为C ++有。他们之所以添加它,是因为他们仔细研究了其他语言,并发现将其作为语言的一部分非常有用。 任何语言功能都可能被滥用。程序员必须始终编写易于阅读和理解的代码。而且,当您的方法具有 unclear 签名时,仅“读取”它们的名称并不能告诉您问题是什么,然后将其归咎于方法名称,而不是类型推断!

答案 1 :(得分:6)

引用Java的Local-Variable Type Inference JEP

  

在像这样的呼叫链中:

int maxWeight = blocks.stream()
                    .filter(b -> b.getColor() == BLUE)
                    .mapToInt(Block::getWeight)
                    .max();
     

没有人打扰(甚至没有注意到)中间类型Stream<Block>IntStream以及lambda形式b的类型未明确出现在源代码中代码。

你烦了吗?

  

如果一个类(例如“存储库”)更改了它的返回类型(例如,从String到int)。使用显式输入时,编译将在调用“存储库”的类上失败。

如果您的示例中有setText之类的重载,则

Repository repository = ...;
myTextView.setText(repository.getFormerlyStringNowInt());

在没有类型推断的情况下也不会失败。要使其失败,您的代码标准必须要求将每个操作的结果分配给局部变量,如

Stream<Block> stream1 = blocks.stream();
Predicate<Block> pred = b -> { 
    Color c = b.getColor();
    return c == BLUE;
};
Stream<Block> stream2 = stream1.filter(pred);
ToIntFunction<Block> getWeight = Block::getWeight;
IntStream stream3 = stream2.mapToInt(getWeight);
int maxWeight = stream3.max();

这时,仅由于可读性降低和偶然使用错误的变量的机会,您就使错误变得更容易。

最后,并不是Kotlin并不是凭空创建的:设计人员可以看到,当C#在2007年引入局部类型推断时,它并没有导致重大问题。或者他们可以看看Scala,它从2004年开始就拥有它。它有(并且有)大量的用户抱怨,但是本地类型推断不是其中之一。