继续学习,这一篇主要是通过scala来吐槽java的,同样是jvm上的语言,差距咋就这么大呢?

作为一个有.NET开发经验的程序员,当初刚接触java时,相信很多人对java语言有以下不爽(只列了极小一部分):

1. 一堆的setter/getter方法,没有c#中的property属性概念

2. 方法的参数值,不能设置缺省值

3. 不定个数参数的写法太单一

...

然后java的拥护者讲出一堆大道理,说这样设计是如何如何有道理,各种洗脑,时间长了,也就被迫习惯了。要不是遇到scala,我还真就信了,你看看人家scala同学,2003/2004发布的,早就把这些全实现了,而java同学作为jvm上的元老,这些年一直顽固不化,不思进取,已经被jvm上的其它同学远远甩在后面了,java你可长点心吧!进入正题,直接上码:

一、参数缺省值

  /**
* 参数缺省值
* @param person
* @param msg
*/
def saySomething(person: String = "somebody", msg: String = "Hello") = {
println(person + " say : " + msg);
}

调用示例:

    saySomething()
saySomething("jimmy")
saySomething("jimmy", "hi")

当然这里有一个小小的限制,如果要用参数缺省值,建议所有的参数全设置缺省值,如果只给部分参数设置缺省值,函数定义不会有问题,调用时,上面的示例编译就通不过了(大意是提供的参数不足之类),大家可以把msg参数的缺省值去掉再试试。

那么,最终编译出来的class,到底是如何实现的呢?可以借助一些反编译工具,比如JD-GUI还原成java一看究竟:

    public void saySomething(String person, String msg) {
Predef..MODULE$.println(new StringBuilder().append(person).append(" say : ").append(msg).toString());
} public String saySomething$default$1() {
return "somebody";
} public String saySomething$default$2() {
return "Hello";
}

也就是说,scala中的def saySomething(person: String = "somebody", msg: String = "Hello") 如果用java实现的话,可以用3个方法来变相实现,每个缺省参数,相当于一个独立的版本,换言之,在编译器层面,其实java的编译器如果想做,是完全可以做到的,为什么不做?懒!顽!

二、class的property

/**
* 定义一个带参主构造器的类
* @param pReadOnly
*/
class Sample(pReadOnly: String) { /**
* 可读写的属性
*/
var myProperty: String = _; private val _readOnly: String = pReadOnly; /**
* 只读属性
*/
def readOnly: String = _readOnly; }

调用示例:

    val sample = new Sample("test")
println(sample.readOnly)
sample.myProperty = "a new value"
println(sample.myProperty)

没了setter/getter看起来倍儿清爽!还是反编译class看看:

public class Sample
{
private String myProperty;
private final String _readOnly; public String myProperty()
{
return this.myProperty; }
public void myProperty_$eq(String x$1) { this.myProperty = x$1; }
private String _readOnly() {
return this._readOnly;
} public String readOnly()
{
return _readOnly();
} public Sample(String pReadOnly)
{
this._readOnly = pReadOnly;
}
}

可以看到,myProperty自动生成了setter/gettter,仍然是在编译器层面,就可以顺手做掉的事情,java编译器依然不肯做。

三、不定个数参数值

这个问题,java中虽然可以xxx(String[] args)用数组传递达到类似的效果,但是就算传一个空数组,也至少也得写一个xxx(null)吧,既然此时参数都为空了,为啥不直接xxx()更直接,看看scala:

  /**
* 不固定个数的参数
* @param x
* @return
*/
def add(x: Int*) = {
var i = 0
for (j <- x) i += j
i
}

调用:

    println(add())
println(add(1, 2, 3, 4, 5))

明显的更高端大气上档次,继续反编译,这个要略复杂点:
先是生成了这么一个类:

public final class DefHello$$anonfun$add$1 extends AbstractFunction1.mcVI.sp
implements Serializable
{
public static final long serialVersionUID = 0L;
private final IntRef i$1; public final void apply(int j)
{
apply$mcVI$sp(j); }
public void apply$mcVI$sp(int j) { this.i$1.elem += j; } public DefHello$$anonfun$add$1(IntRef i$1)
{
}
}

然后是:

   public void main(String[] args)
{
Predef..MODULE$.println(BoxesRunTime.boxToInteger(add(Nil..MODULE$)));
Predef..MODULE$.println(BoxesRunTime.boxToInteger(add(Predef..MODULE$.wrapIntArray(new int[] { 1, 2, 3, 4, 5 }))));
...
} public int add(Seq<Object> x)
{
IntRef i = IntRef.create(0);
x.foreach(new AbstractFunction1.mcVI.sp() { public static final long serialVersionUID = 0L; public final void apply(int j) { apply$mcVI$sp(j); }
public void apply$mcVI$sp(int j) { DefHello..this.elem += j; } });
return i.elem;
}

最终调用时,add()这里虽然scala没有传任何参数,但从反编译结果上看,最终还是变成了add(Nil..MODULE$)),编译器自动加了一个参数,以满足java的规范。

四、泛型初步

java中的泛型是一个"伪"泛型,其类型擦除机制只是障眼法而已,因此带来了很多使用上的限制,比如下面这个例子:

public class SampleClass<T> {
private T _t; public SampleClass(T t) {
this._t = t;
} public T getT() {
return _t;
}
}

这里定义了一个泛型类,如果想创建一个该类的数组:

SampleClass<String>[] objs = new SampleClass<String>[10];

编译器会直接报错:Error: java: generic array creation,原因是:type erase后,内部已经是SampleClass[],按OOP的原则,可以向上转型为Object[],这下可好了,Object是万能类型,如果向这个万能类型的数组里加入一个不是SampleClass<String>的实例,理论上也是允许的,这就违背了泛型约束的初衷。

但是在scala中,却是可以这样做的,看下面的代码:

class MyClass[T](t1: T) {
var t: T = t1;
}

然后可以这样用:

    val objs = new Array[MyClass[String]](10)
objs(0) = new MyClass[String]("a")
for (x <- objs; if x != null) println(x.t)

编译和运行一切正常,这是什么情况?还是反编译解密:

    MyClass[] objs = new MyClass[10];

    objs[0] = new MyClass("a");

    Predef..MODULE$.refArrayOps((Object[])objs).withFilter(new DefHello..anonfun.main.1()).foreach(new DefHello..anonfun.main.2());

原来,对于java的伪泛型机制,scala早就看穿了这一切,因此它采用了一种略带"极端"的做法,直接使用原始类型,无情的对java的泛型机制回应:『不约,我们不约』。

了解以上这些后,我不得不更加佩服坚持使用java语言写出这么多NB开源框架的达人们,硬是用一个要啥啥没有的语言为开源世界做出这么大的贡献,这是一种什么样的精神,无禁让我想起了《道士下山》中猿击术中的精髓:"不离不弃,不嗔不恨!",我只想说:这么多年,你们是怎么忍下来的!

So,Scala既然这么好,就完美无缺了么?当然不是,功能越强大,语法越灵活,自然学习成本也更高。另外,性能方面,它生成的字节码感觉比java略多,网上有很多关于scala与java的性能讨论,包括google也有类似的评测,有人说这二者差不多,但是多数人还是认为在jvm上,scala的性能整体来看要低于java,只能达到java的8成上下(详情可自行百度,有很多这类文章)

scala 学习笔记(03) 参数缺省值、不定个数参数、类的属性(Property)、泛型初步的更多相关文章

  1. 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性

    基于.net的分布式系统限流组件   在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...

  2. OpenCV 学习笔记03 findContours函数

    opencv-python   4.0.1 1 函数释义 词义:发现轮廓! 从二进制图像中查找轮廓(Finds contours in a binary image):轮廓是形状分析和物体检测和识别的 ...

  3. SaToken学习笔记-03

    SaToken学习笔记-03 如果排版有问题,请点击:传送门 核心思想 所谓权限验证,验证的核心就是一个账号是否拥有一个权限码 有,就让你通过.没有?那么禁止访问! 再往底了说,就是每个账号都会拥有一 ...

  4. 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试

    机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...

  5. OpenCV 学习笔记03 边界框、最小矩形区域和最小闭圆的轮廓

    本节代码使用的opencv-python 4.0.1,numpy 1.15.4 + mkl 使用图片为 Mjolnir_Round_Car_Magnet_300x300.jpg 代码如下: impor ...

  6. Scala学习笔记及与Java不同之处总结-从Java开发者角度

    Scala与Java具有很多相似之处,但又有很多不同.这里主要从一个Java开发者的角度,总结在使用Scala的过程中所面临的一些思维转变. 这里仅仅是总结了部分两种语言在开发过程中的不同,以后会陆续 ...

  7. Redis:学习笔记-03

    Redis:学习笔记-03 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 7. Redis配置文件 启动 ...

  8. C++ GUI Qt4学习笔记03

    C++ GUI Qt4学习笔记03   qtc++spreadsheet文档工具resources 本章介绍创建Spreadsheet应用程序的主窗口 1.子类化QMainWindow 通过子类化QM ...

  9. Directx11学习笔记【二十一】 封装键盘鼠标响应类

    原文:Directx11学习笔记[二十一] 封装键盘鼠标响应类 摘要: 本文由zhangbaochong原创,转载请注明出处:http://www.cnblogs.com/zhangbaochong/ ...

随机推荐

  1. js 事件处理程序 事件对象

    事件:用户或浏览器自身执行的动作: 事件处理程序:响应某个事件的函数: 事件流:从页面中接收事件的顺序. 1.DOM事件流 "DOM2级事件"规定的事件流包括三个阶段:事件捕获阶段 ...

  2. 结对编程-地铁续(有种上个学期OO的既视感)

    我们组比较特殊..三人结对 github:https://github.com/qingchanghan/WPFUI_Metro po一张照片: 石浩然,韩青长.陈彦吉 (台式机真的很高端,分屏贼帅) ...

  3. Asp.Net MVC 自定义的MVC框架(非EF操作数据库)

    一些废话:在北京辞职回家不知不觉中已经半年多了,这半年中有过很多的彷徨,困惑,还有些小小难受.半年时间算是我人生以来遇到过的最困苦的时候.理想的工作跟我擦肩而过,驾照也没有考过,年后这一改革...,毕 ...

  4. nodejs学习笔记(1)--express安装问题:express不是内部也或者外部的命令解决方案

    "Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具.使用 Express 可以快速地搭建一个完 ...

  5. char(10)和VARCHAR(10)主要的区别是什么?

    区别: 1.CHAR的长度是固定的,而VARCHAR2的长度是可以变化的, 如: 存储字符串“abc", 对于CHAR (10),表示你存储的字符将占10个字节(包括7个空字符), 而同样的 ...

  6. 简单的 http 服务器

    HttpUtils: package org.windwant.httpserver; import java.io.IOException; import java.net.InetSocketAd ...

  7. jquery checkbox操作

    一.通过选择器选取CheckBox: 1.给CheckBox设置一个id属性,通过id选择器选取: <input type="checkbox" name="myB ...

  8. vs2013外接程序”VMDebugger”加载异常处理

    cmd → regedit HKEY_LOCAL_MACHINE → OFTWARE → Wow6432Node → Microsoft  → VisualStudio → 12.0 → AddIns ...

  9. x01.Game.Main: 从零开始

    一切从零开始,一切皆有可能. 浅墨,90后,<逐梦之旅>深入浅出,堪比大师. 1.安装 DXSDK_June10.exe 或更新版本. 2.运行 vs2012,新建 VC Win32 空项 ...

  10. JavaScript中变量提升是语言设计缺陷

    首先纠正下,文章标题里的 “变量提升” 名词是随大流叫法,“变量提升” 改为 “标识符提升” 更准确.因为变量一般指使用 var 声明的标识符,JS 里使用 function 声明的标识符也存在提升( ...