Calling any method on Nothing

2019-08-12 16:59发布

问题:

Although it is not explicitly stated that Nothing is the subtype of all types, this (among others) suggests it:

fun f(x:Float) { }
fun g(x:Char) { }

fun dead(q: Nothing) {
    f(q)
    g(q)
}

However, this fails with “unresolved reference”:

fun dead(q: Nothing) {
    q.not()
}

Is this a bug or a feature?

Notes:

  1. First piece of code compiles (with warnings), second does not
  2. It's possible to use a Nothing typed receiver, for instance calling toString()
  3. This is legal: {b:Boolean -> b.not()}(q)
  4. Upcast too: (q as Boolean).not()
  5. Equivalent question for Scala

回答1:

Nothing is Nothing for a reason. You can't call any functions on it. Besides not() is only applicable for Boolean so it is not present on Nothing. In fact there are no methods on Nothing:

/**
 * Nothing has no instances. You can use Nothing to represent "a value that never exists": for example,
 * if a function has the return type of Nothing, it means that it never returns (always throws an exception).
 */
public class Nothing private constructor()

The documentation pretty much explains its existence.

There is one loophole though. What happens if you return Nothing? from a function?

fun dead(): Nothing? {
    return null
}

That's right. It can only return null:

@JvmStatic
fun main(args: Array<String>) {
    dead() // will be null
}

I wouldn't say that there is a valid use case to do this but it is possible.

An example for Nothing to indicate nothingness in trees:

sealed class Tree<out T>() {
    data class Node<out T>(val value: T,
                           val left: Tree<T> = None,
                           val right: Tree<T> = None): Tree<T>()
    object None: Tree<Nothing>()
}

Here Nothing denotes a leaf node with no children.



回答2:

The premise itself doesn't make sense. Nothing is a class which can not be instantiated. You'll never have a variable that holds a Nothing instance.

This means that a function that takes Nothing as a parameter can never be invoked, because you can't get a Nothing instance to pass to it. Anything you write inside it is irrelevant, as is a function like this existing in the first place.

Nothing is made to act like a subtype to all types so that certain uses of language features like throw and return work nicely with the type system. In essence, the compiler lets you pass a Nothing in places where some other type is required, because it knows that you'll never actually reach that code (because again, you can't get a Nothing instance), so it doesn't matter what you're passing in.