看到Scala特质,感觉用起来应该挺方便,把需要在Java中分开写的代码,直接写在一起,实现起来,以及看起来,都挺好的。
特质可以提供方法和字段的实现。
Scala特质这玩意看起来又像接口,又像抽象类,到底是怎么回事?Scala编译器对特质到底做了什么?
通过代码来看应该比较容易。
定义一个Logger特质:
package gao.traits trait Logger { def log(msg:String) def info(msg:String): Unit = { log("INFO:" + msg) } def warn(msg:String): Unit = { log("WARN:" + msg) } def error(msg:String): Unit = { log("ERROR:" + msg) } }
实现ConsoleLogger扩展Logger特质:
package gao.traits import java.util.Date class ConsoleLogger extends Logger{ override def log(msg: String): Unit = { println(new Date() + " " + msg) } }
不管怎么说,看起来是比Java方便的。
在Java中,要实现上边两段代码相同的功能,基本需要分三步完成:
(1)定义接口Logger
(2)定义抽象类AbstractLoggerimplements Logger,并实现info、warn、error三个方法
(3)实现ConsoleLoggerextends AbstractLogger implements Logger
实现过程就像Java的集合框架实现流程。
到底Scala是怎么两步完成Java需三步才能完成的代码的呢?我们来看看编译后的.class文件吧。
上边两段代码会被Scala编译成3个.class文件,如下:
$ ls target/scala-2.11/classes/gao/traits/
ConsoleLogger.class
Logger.class
Logger$class.class
使用javap命令来看一看这些.class文件都长什么样子。
$ javap -p target/scala-2.11/classes/gao/traits/Logger.class Compiled from "Logger.scala" public interface gao.traits.Logger { public abstract void log(java.lang.String); public abstract void info(java.lang.String); public abstract void warn(java.lang.String); public abstract void error(java.lang.String); }
$ javap -p target/scala-2.11/classes/gao/traits/Logger\$class.class Compiled from "Logger.scala" public abstract class gao.traits.Logger$class { public static void info(gao.traits.Logger, java.lang.String); public static void warn(gao.traits.Logger, java.lang.String); public static void error(gao.traits.Logger, java.lang.String); public static void $init$(gao.traits.Logger); }
$ javap -p target/scala-2.11/classes/gao/traits/ConsoleLogger.class Compiled from "ConsoleLogger.scala" public class gao.traits.ConsoleLogger implements gao.traits.Logger { public void info(java.lang.String); public void warn(java.lang.String); public void error(java.lang.String); public void log(java.lang.String); public gao.traits.ConsoleLogger(); }
OK,看起来清晰多了吧,要在JVM上运行,最终都是一样的,只是Scala编译器简化了代码编辑工作量,而且Scala特质使代码实现起来更合乎人的思维逻辑。
编译后,ConsoleLogger并没有继承gao.traits.Logger$class,可以想象只是调用了其静态方法。所以在Scala中,类可以实现任意数量的特质,看起来就像多继承一样。而在Java规范中,是不允许多继承的(因为可能导致JVM无法选择吧)。
前几天看了一点点Java8的东西,看到在Java8中,接口也可以有默认方法实现,比如在java.util.Collection接口及其子接口中都添加了一些实现方法,如下是java.util.Collection接口新增的代码:
/** * Returns a sequential {@code Stream} with this collection as its source. * * <p>This method should be overridden when the {@link #spliterator()} * method cannot return a spliterator that is {@code IMMUTABLE}, * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()} * for details.) * * @implSpec * The default implementation creates a sequential {@code Stream} from the * collection's {@code Spliterator}. * * @return a sequential {@code Stream} over the elements in this collection * @since 1.8 */ default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
这样,在Java8也能像Scala定义特质一样定义接口了。帮帮哒!!!
虽然没有看到,想象Java8应该为此修改、新增了JVM规范的内容,使JVM能接受既有抽象方法又有具体方法的接口;
或者,仅仅是修改了Java编译器,来实现上边描述的Scala编译器功能,如果这样,看起来好像低级了一点,简单粗暴的静态方法调用来组合功能,想想都混乱。
接下来打算学习学习Java8,看起来蛮吊的。