kotlin避免空指针

by Adam Arold

亚当·阿罗德(Adam Arold)

Kotlin陷阱以及如何避免它们 (Kotlin pitfalls and how to avoid them)

Kotlin is all the rage lately. And while I do agree that the language is well thought out, it does have — as with everything else — its flaws.

Kotlin最近风靡一时。 尽管我确实同意该语言经过深思熟虑,但与其他所有事物一样,它确实存在缺陷。

In this article I’ll explain some of the pitfalls I encountered and try to help you avoid them.

在本文中,我将解释我遇到的一些陷阱,并尝试帮助您避免这些陷阱。

null之谜 (The mystery null)

In Kotlin you can write your code as if null never existed and this can make you forget that null is omnipresent but it hides. Let's look at this simple and seemingly innocent class:

在Kotlin中,您可以将代码编写为好像从未存在过null ,这会让您忘记null无处不在,但它会隐藏。 让我们看一下这个简单的看似纯真的类:

If you try to instantiate this, you’ll get a NullPointerException because bar tried to access the length of c before it was initialized.

如果尝试实例化此实例,则会得到NullPointerException因为bar试图在初始化c之前访问其length

Of course the application logic was flawed here, but you still got an Exception. The worst part of this is that your IDE won't complain about this.

当然,这里的应用程序逻辑有缺陷,但是您仍然会遇到Exception 。 最糟糕的部分是您的IDE不会对此抱怨。

The takeaway here is that Kotlin will help you in a lot of cases (nearly all) to avoid null, but you can't forget about it and from time to time you'll encounter things like this.

这里的要点是,Kotlin 在很多情况下(几乎所有情况下)为您提供帮助 ,以避免null ,但是您无法忘记它,并且您会不时遇到这样的情况。

从JDK处理null (Handling nulls from the JDK)

Kotlin’s standard library handles nulls fine. But if you use classes from the JDK, you will have to handle possible null pointers from library functions by hand.

Kotlin的标准库可以处理null 。 但是,如果您使用JDK中的类,则必须手动处理库函数中可能的null指针。

Most of the time the Kotlin classes are enough, but sometimes you have to use something like the ConcurrentHashMap:

大多数时候,Kotlin类足够了,但是有时您必须使用类似ConcurrentHashMap

In this case, you have to use the !! operator. But in other cases the null safety operator (?) can also work. Nevertheless, if you use Java libraries extensively you'll have to litter your code with !!s and ?s or write adapters for Java classes. This is something you can't really avoid.

在这种情况下,您必须使用!! 操作员。 但是在其他情况下, null安全运算符( ? )也可以工作。 但是,如果您广泛使用Java库,则必须使用!! s和? 或Java类的写适配器。 这是您无法避免的事情。

There’s another more hideous problem you might bump into. When using methods on JDK classes, they can return null and don’t have syntactic sugar like the Map access above.

您可能还会遇到另一个更可怕的问题。 在JDK类上使用方法时,它们可以返回null并且不像上面的Map访问那样具有语法糖。

Consider the following example:

考虑以下示例:

In this case, you use peek which in fact can return null. But the Kotlin compiler won't complain so you can get a NullPointerException if your Queue was empty.

在这种情况下,您可以使用peek实际上可以返回null 。 但是Kotlin编译器不会抱怨,因此如果您的Queue为空,则可以获取NullPointerException

The problem here is that we used Queue which is a JDK interface and if you look at the implementation of peek:

这里的问题是,我们使用Queue这是一个JDK接口,如果您看一下peek的实现,请执行以下操作:

It says that peek will return E which will lead Kotlin to believe that E is not nullable. This might be worked around in a future version of Kotlin, but right now it is important to keep this in mind in your projects and use interfaces like this:

它说peek将返回E ,这将导致Kotlin相信E不能为空。 将来的Kotlin版本中可能会解决此问题,但现在在您的项目中牢记这一点并使用如下界面非常重要

it (The inner it)

When a lambda has a single parameter you can omit it from your code and can use it instead:

当lambda具有单个参数时,您可以从代码中省略它,而可以使用it来代替:

“it: implicit name of a single parameter One other helpful convention is that if a function literal has only one parameter, its declaration may be omitted (along with the ->), and its name will be it.” — Kotlin docs

“它:单个参数的隐式名称另一个有用的约定是,如果函数文字只有一个参数,则可以省略其声明(连同->一起使用,其名称将为它。” — Kotlin d ocs

The problem with this is when you have nested functions like in this example:

问题是当您具有以下示例中的嵌套函数时:

It only takes a few its to lose track which is which. The solution to this problem is to name the parameters explicitly:

只需花几下it可以弄清哪个是哪个。 解决此问题的方法是显式命名参数:

Much better!

好多了!

阴险的copy (The insidious copy)

Take a look at this data class:

看一下这个data class

Data classes give you a bunch of functions and you can also make a copy of them. Guess what this will print out:

数据类为您提供了一堆函数,您也可以copy它们。 猜猜这将打印出什么:

This will print foobar, wombar, oops. The problem is that while the name indicates that copy will make an actual copy in fact it will only copy the references in your object. This can be insidious if you forget to write an unit test and you pass your data classes around as if they were immutable value objects.

这将打印foobar, wombar, oops 。 问题在于,尽管名称指示copy实际上将创建实际副本 ,但它只会复制对象中的引用。 如果您忘记编写单元测试并且将data class es传递为好像它们是不可变的值对象 s,则这可能是隐患。

The solution to this problem is to pay attention to your data classes and if they should be value objects make them one:

解决此问题的方法是注意您的data class es,如果它们应该是值对象,则使其成为一个:

There is an other problem with data classes: you can't tell Kotlin which fields you want to put in your equals / hashCode, you can only override both and write them by hand. Keep this in mind.

data class es还有另一个问题:您无法告诉Kotlin您想在equals / hashCode放入哪些字段,您只能override这两个字段并手动编写它们。 请记住这一点。

内部泄漏 (Internal leakage)

Take a look at this example:

看一下这个例子:

If you use classes like this from other Kotlin projects the internal keyword will be respected. If you look at this from a Java project however hiddenOperation will be public! To avoid this I'd suggest using interfaces to hide implementation details:

如果您在其他Kotlin项目中使用类似的类,则将使用internal关键字。 如果您从Java项目hiddenOperation这个问题,那么hiddenOperation将是public ! 为了避免这种情况,我建议使用interface来隐藏实现细节:

非通用全局扩展 (Non-generic global extensions)

The utility of extension functions is unquestionably high, but with great power comes great responsibility. You can — for example — write extension functions to JDK classes which will be visible for the whole project. This can be problematic when they are non-generic and represent operations which only make sense in a local context:

扩展功能的效用无疑是很高的,但是功能强大,责任重大。 您可以-例如-向JDK类编写扩展功能,这些功能对于整个项目都是可见的。 当它们是非泛型的并且表示仅在本地上下文中有意义的操作时,这可能会成问题:

Now everybody on your project will scratch their heads when they bump into this. So I think it is good if you think twice before you write extension functions but they can be really powerful. here are some examples which might be useful:

现在,项目中的每个人都会碰到这个问题。 因此,我认为在编写扩展功能之前三思而后行很好,但是它们确实很强大。 以下是一些可能有用的示例:

单位返回lambdas与Java SAM转换 (Unit returning lambdas vs Java SAM conversion)

When you have functions which accept lambdas you can omit the return keyword if the lambda's return type is Unit:

当您具有接受lambda的函数时,如果lambda的返回类型为Unit则可以省略return关键字:

This is fine but if you call this from Java you’ll face the awkward problem of needing to return Unit:

很好,但是如果您从Java调用它,则会遇到需要返回Unit的尴尬问题:

This is very clunky from the Java side. If you try to make this work from Java you can define an interface:

从Java方面来看,这非常笨拙。 如果尝试使用Java进行此工作,则可以定义一个interface

then you can use Java’s SAM conversion to make this very simple:

那么您可以使用Java的SAM转换使此过程非常简单:

but then from the Kotlin side it becomes a mess:

但是从Kotlin的角度来看,情况变得一团糟:

The problem is that Kotlin does not support SAM conversion for Kotlin classes and it only works with Java classes. My suggestion is that for simple cases just use Java’s built in SAM interfaces like Consumer:

问题是Kotlin不支持Kotlin类的SAM转换,并且仅适用于Java类。 我的建议是,在简单情况下,只需使用Java内置的SAM接口(例如Consumer

Java互操作与不可修改的集合 (Java interop with unmodifiable Collections)

Kotlin gives you immutable variants of the JDK’s collection classes:

Kotlin为您提供了JDK集合类的不变变体:

This is a very nice addition but if you look at this from the Java side you’ll see the JDK’s Set api:

这是一个非常不错的补充,但是如果您从Java的角度看这件事,您会看到JDK的Set api:

If you try to modify this Set the same will happen as if you have used Java's Collections.unmodifiableSet() method. I don't know whether this can be (or should be) worked around but this is something you can keep in mind when working with Kotlin's immutable versions of Java collections.

如果尝试修改此Set ,则将发生与使用Java的Collections.unmodifiableSet()方法相同的情况。 我不知道是否可以(或应该)解决此问题,但这是您在使用Kotlin的不可变版本的Java集合时要牢记的。

接口无过载 (No overloads in interfaces)

This is only an issue from an interop perspective, but Kotlin does not support the @JvmOverloads annotation in an interfaceand you can't use it on overrides either:

从互操作性的角度来看,这只是一个问题,但是Kotlin不支持interface@JvmOverloads注释,并且您也不能在override s上使用它:

Currently the only thing you can do to have overloads is to define them by hand:

当前,您唯一可以做的重载就是手工定义它们:

Keep it in mind though that you can suggest improvements to Kotlin itself using KEEP (Kotlin Evolution and Enhancement Process). KEEP is something like JEP in Java, but of course KEEP has much less red tape compared to JEP.

请记住,尽管您可以建议使用KEEP (Kotlin进化和增强过程)对Kotlin本身进行改进。 KEEP类似于Java中的JEP,但是与JEP相比,KEEP当然没有很多繁文tape节。

结论 (Conclusion)

Kotlin is a very popular language right now and I do agree that it is a Turbo Java, but you should take any hype with a grain of salt. As we have seen above Kotlin has its own pitfalls which you should be aware of if you plan to use it.

Kotlin现在是一种非常流行的语言,我确实同意它是Turbo Java,但是您应该大肆宣传。 正如我们在上面看到的,Kotlin有其自身的陷阱,如果您打算使用它,您应该知道。

All in all I think that the problems mentioned above can either be worked around easily or not critical and they don’t limit the usability of the language itself.

总而言之,我认为上述问题可以轻松解决,也可以解决不严重的问题,并且不限制语言本身的可用性。

Thanks for reading! You can read more of my articles on my blog.

谢谢阅读! 您可以在我的博客上阅读更多我的文章。

翻译自: https://www.freecodecamp.org/news/kotlin-pitfalls-and-how-to-avoid-them-7b0d3a2109ad/

kotlin避免空指针

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐