scala(⼀)Nothing、Null、Unit、None、null、Nil理解
相对于java的类型系统,scala⽆疑要复杂的多!也正是这复杂多变的类型系统才让OOP和FP完美的融合在了⼀起!
Nothing:
如果直接在scala-library中搜索Nothing的话是不到了,只能发现⼀个Nothing$的类(后⾯再说Nothing$和Nothing的关系)。要想看到Nothing.scala的源码需要去github上的scala源码中查可以看到在Nothing.scala中只是定义了⼀个sealed trait:
package scala
/** `Nothing` is - together with [[scala.Null]] - at the bottom of Scala's type hierarchy.
*
* `Nothing` is a subtype of every other type (including [[scala.Null]]); there exist
* ''no instances'' of this type. Although type `Nothing` is uninhabited, it is
* nevertheless useful in several ways. For instance, the Scala library defines a value
* [[llection.immutable.Nil]] of type `List[Nothing]`. Because lists are covariant in Scala,
* this makes [[llection.immutable.Nil]] an instance of `List[T]`, for any element of type `T`.
*
* Another usage for Nothing is the return type for methods which never return normally.
* One example is method error in [[scala.sys]], which always throws an exception.
*/
sealed trait Nothing
在这⾥,Nothing没有任何实例,其类似于java中的标⽰性接⼝(如:Serializable,⽤来标识该该类可以进⾏序列化),只是⽤来标识⼀个空类型。根据官⽅注释可以看出来Nothing⽤来标识no instances类型,该类型是其它所有类型的⼦类型。官⽅注释中给了如下两个⽤途: 1、⽤来当做Nil的类型List[Nothing]
case object Nil extends List[Nothing] {...}
Nil表⽰⼀个空的list,与list中的元素类型⽆关,他可以同时表⽰List[任意类型]的空集合。也即是Nil可以同时表⽰List[Int]类型的空集合和List[String]类型的空集合。那么考虑⼀下Nil:List[?],这⾥的“?”应该为什么类型呢?也即是“?”应该为Int、所有类型的⼦类型(List集合为协变的)。因此这⾥引⼊了Nothing类型作为所有类型的⼦类型。这样Nil:List[Nothing]就可以完美实现上述需求。
2、表⽰⾮正常类型的返回值类型
例如Nil中的两个⽅法:
override def head: Nothing = throw new NoSuchElementException("head of empty list")
override def tail: List[Nothing] = throw new UnsupportedOperationException("tail of empty list")
Nil为空List,所以调⽤head和tail应该返回Nothing和List[Nothing]的实例。但是Nothing是没有实例的,这⾥就直接抛出Exception。所以这⾥就使⽤Nothig来表⽰throw .... ⾮正常返回的类型。⾮正常即发⽣了错误没有返回任何对象,连Unit都没有,⽤Nothing类表⽰确实也挺合适。
明⽩了Nothing的表⽰的含义以及Nothing的应⽤场景,那么,Nothing是如何⼯作的呢?Nothing和Nothing$之间⼜有什么关系呢?
分别对Nothing.scala和Nothing$.scala进⾏编译和反编译:
Nothing.scala
Nothing$.scala
编译之后的⽂件名
根据上⾯的结果来看,Nothing.scala编译之后是⼀个接⼝类型:
public abstract interface Nothing {}
⽽Nothing$.scala编译之后是⼀个抽象类:
public abstract class Nothing$ extends Throwable{}
从这⾥看两者没有任何关系。但是在Nothign$.scala的源码中有⼀段注释:
package scala
package runtime
/
**
* Dummy class which exist only to satisfy the JVM. 虚拟类,只存在于JVM中
* It corresponds to `scala.Nothing`. 它对应scala.Nothing
* If such type appears in method signatures, it is erased to this one. 如果该Nothing出现在⽅法签名中则将会被抹掉,然后替换为Nothing$
*/
sealed abstract class Nothing$ extends Throwable
这⾥阐明了Nothing$的作⽤,也即是代码中如果出现Nothing类型的时候,在load到JVM运⾏的时候将会把Nothing替换为Nothing$类型。也即在JVM之外以Nothing的⾝份进⾏显⽰,在JVM中以Nothing$的⾝份进⾏显⽰,两者表⽰同⼀个含义。
这样解释也满⾜了Nothing可以当做throw new XXXXXException("head of empty list")的类型使⽤的原因,即: Nothing$ extends Throwable.
Null:
Null.scala的源码和Nothing.scala的源码在同⼀个包中存在着对⽐⼀下两者:
Null有唯⼀的实例null Nothing没有任何实例
Null是所有引⽤类型(AnyRef)的⼦类型 Nothing是所有类型的⼦类型 因此=> Nothing是Null 的⼦类型
除了上⾯的两点区别之外,Null和Nothing⼏乎⼀致
Null.scala 源码:
package scala
/** `Null` is - together with [[scala.Nothing]] - at the bottom of the Scala type hierarchy.
*
* `Null` is a subtype of all reference types; its only instance is the `null` reference.
* Since `Null` is not a subtype of value types, `null` is not a member of any such type. For instance,
* it is not possible to assign `null` to a variable of type [[scala.Int]].
*/
sealed trait Null
注释中也明确说明Null是所有引⽤类型的⼦类型,只有唯⼀个⼀个实例null。trait Null 说明其实⼀个类型,那么就可以⽤该类型定义字
段:val myNull:Null=null
nullpointerexception为什么异常那么注释中说Null有唯⼀的⼀个实例,那么我们new⼀个Null如何呢?
这⾥提⽰Null是⼀个abstract抽象类,可源码中定义的Null是⼀个trait,那么这⾥在运⾏的时候为何会提⽰Null is abstract呢?其次在和Nothing$.scala 旁边同样存在⼀个Null$.scala,这个类和Null.scala⼜有什么关系呢?
正如你想象的那样,Null$.scala正是Null在JVM中的另⼀种⾝份。我们看⼀下Null$.scala 的源码:
package scala
package runtime
/
**
* Dummy class which exist only to satisfy the JVM. 该类为虚拟类,只存在JVM
* It corresponds to `scala.Null`. 它对应着scala.Null
* If such type appears in method signatures, it is erased to this one. 如果Null出现在⽅法签名中则⽤Null$去替换
* A private constructor ensures that Java code can't create subclasses. private构造⽅法确保不会被创建其它实例
* The only value of type Null$ should be null 唯⼀个实例及时null
*/
sealed abstract class Null$ private ()
这⾥明确指出Null将会被Null$替换,那么在运⾏的时候Null便为Null$类型!原来上⾯提⽰Null is abstract是这个原因!!我们再进⼀步确认⼀下,查看⼀下Null$.scala 进⾏编译看编译之后的代码:
对Null$.scala进⾏编译然后反编译结果:
(注意:在javap查看代码的时候如果是private 构造参数则不会显⽰处理,如果是public则会直接显⽰处理,上⾯没有显⽰private Null$()正说明Null$的构造参数正是private类型的)
到这⾥就完全就明⽩了,Null在运⾏期间被替换了Null$.
Unit:
在解释Unit之前需要分析⼀下函数的返回值有⼏种可能性!
1、正常返回了数据,例如 def demo01():Int=10 def demo02():String="hello"
2、⽅法发⽣异常,没有执⾏结束,未进⾏返回操作。例如 def demo02():Nothing=throw new NoSuchElementException("head of empty list")
3、⽅法返回了⼀个类型X的实例,该类型X表⽰ “没有返回值”的类型。这⾥有些绕,要仔细理解“没有返回值”和“没有返回值类型”。⽽这⾥的X即是Unit,⽽返回的那个实例即(),在java中表⽰未void。例如: def demo03():Unit={println("hello")}
对于1很好理解,返回什么就接受什么类型。对于2便是上⾯说的Nothing类型,没有进⾏返回操作;对于3是有返回的,只是返回的内容表⽰“没有返回值”的类型。
在源码中很好的解释了Unit的作⽤:
/** `Unit` is a subtype of [[scala.AnyVal]]. There is only one value of type
* `Unit`, `()`, and it is not represented by any object in the underlying
* runtime system. A method with return type `Unit` is analogous to a Java
* method which is declared `void`.
*/
final abstract class Unit private extends AnyVal {
// Provide a more specific return type for Scaladoc
override def getClass(): Class[Unit] =
}
根据注释可以看出,Unit是所有AnyVal 的⼦类(注意区别Nothing),只有⼀个唯⼀的Value(注意这⾥是Value依旧是实例/对象)。如果⽅法的返回值类型为Unit,则类似于java中void。
在Unit的伴⽣对象中则揭开了Unit和void的关系:
object Unit extends AnyValCompanion {
/** Transform a value type into a boxed reference type.
*
* @param x the Unit to be boxed
* @return a scala.runtime.BoxedUnit offering `x` as its underlying value.
*/
def box(x: Unit): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT
/** Transform a boxed type into a value type. Note that this
* method is not typesafe: it accepts any Object, but will throw
* an exception if the argument is not a scala.runtime.BoxedUnit.
*
* @param x the scala.runtime.BoxedUnit to be unboxed.
* @throws ClassCastException if the argument is not a scala.runtime.BoxedUnit
* @return the Unit value ()
*/
def unbox(x: java.lang.Object): Unit = x.asInstanceOf[scala.runtime.BoxedUnit]
/** The String representation of the scala.Unit companion object. */
override def toString = "object scala.Unit"
}
请注意box()和unbox()⽅法,该⽅法是对数值类型进⾏装箱和拆箱操作,scala中所有的数值型类型类中都有这两种⽅法,主要⽤来数值型向java 数值的封装型转化,例如:int->Integer float->Float
那么Unit中的box()和unbox()也是进⾏拆装箱操作了,我们看到在Unit的unbox中把java.lang.Object类型转换为⼀个BoxeUnit类型的数据,那么这个BoxedUnit是什么类型呢?我们打开源码看⼀下:
package scala.runtime;
public final class BoxedUnit implements java.io.Serializable {
private static final long serialVersionUID = 8405543498931817370L;
public final static BoxedUnit UNIT = new BoxedUnit();
public final static Class<Void> TYPE = java.lang.Void.TYPE;
private Object readResolve() { return UNIT; }
private BoxedUnit() { }
public boolean equals(java.lang.Object other) {return this == other;}
public int hashCode() { return 0;}
public String toString() {return"()";}
}
可以看到其TYPE值直接指向了java的void: public final static Class<Void> TYPE = java.lang.Void.TYPE;
看来scala只是使⽤Unit对java中的void进⾏了包装,⽤Unit来表⽰void的类型,⽤BoxedUnit的单例对象来表⽰那个唯⼀的void,也即是Unit中的“()”。到这⾥才明⽩Unit是没有任何实例的,它只是起⼀个类型的作⽤,⽽那个“()”也只是BoxedUnit的单例对象toSting的输出内容⽽已。 None:
在说None之前需要了解Option类型。Option类型是对值进⾏封装,根据值是否为null来返回Some(value)或者None:
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
Option的apply()⽅法可以返回None/Some可知None或Some必定是Option的⼦类了。看三者的定义:
sealed abstract class Option[+A] extends Product with Serializable {}//注意sealed 关键字
//class
final case class Some[+A](value: A) extends Option[A] {
def isEmpty = false
def get = value
}
//Object
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("")
}
Option[+A]前⾯有sealed 关键字,则Option[+A]的所有⼦类必须在同⼀个⽂件中定义。因此Option只有Some和None两个⼦类。注意上⾯对Some和None的定义,Some是Class,⽽None是Object。class容易理解,是可以new实例的,⽽Object类型在编译之后构造⽅法是private,⽆法在外部⽣成实例对象,⽽其中的⽅法编译之后变为static的静态⽅法,可以通过类名直接调⽤。另外,在对Object进⾏编译的时
候会同时⽣成⼀个XXXX$.class的⽂件,该类是⼀个单例类,内部会构造⼀个 public static XXXX$ MODULE$; 单例对象。None也同样如此:
因此我们平时所使⽤的None其实就是这个public static scala.None$ MODULE$;单例对象。在应⽤层⾯上我们只需要知道None就是⼀
个Option[Nothing]类型的对象,调⽤get()⽅法将会抛出NoSuchElementException("")异常,其存在的⽬的是为了表⾯java中
的NullPointerException()的发⽣。
null:
null 就很容易理解了和java中的null是同⼀个null。⼀般在scala中不直接使⽤null!
Nil:
看⼀下源码:
case object Nil extends List[Nothing] {....}
根据object便可知Nil是⼀个单例对象,必定存在⼀个Nil$.class,在解压的scala-library中到Nil$.class进⾏反编译可以到Nil$的单例对象:
可以看到 llection.immutable.Nil$ llection.immutable.List<scala.runtime.Nothing$> ,说明Nil是List[Nothing]的⼦类。⽽在源码中的⽅法则直接表明了Nil的性质:⼀个没有元素的List集合:
override def isEmpty = true//集合为空
override def head: Nothing = throw new NoSuchElementException("head of empty list")//抛出NoSuchElementException异常
override def tail: List[Nothing] = throw new UnsupportedOperationException("tail of empty list")//抛出UnsupportedOperationException异常
=========================================
原⽂链接:转载请注明出处!
=========================================
-----end
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论