Java 语言的几个缺陷之三: 不支持 var 类型推断

虽说 Java 的支持泛型以及近期 Lambda  表达式的加入, 在对类型进行推断上已经很强大了, 但在类型声明的时候仍然略显冗余, 最主要的一点是 Java 不能像 Scala 那样在声明变量有赋值的情况下进行类型推断. 我们先来看下 Java  已经为我们所进行的改进:

List<String> strings = new ArrayList<String>();  //刚引入泛型时我们是这样
List<String> strings = new ArrayList<>();             //后来变成这样了, 可以钻石符号推断, Java 7

List<String> list = new ArrayList<>();
list.addAll(new ArrayList<>()); //根据 list.addAll() 上下文推断要创建的类型是 new ArrayList<String>(), Java 8

interface Foo {
  void dodo();
}
Foo foo = () -> System.out.println("Foo:dodo()");   //Java 8 Lambda 可以根据 Lambda 表达式的签名推断出接口类型
foo.dodo();

在 Java 7 的泛型方法其实也是可以通过声明类型与方法参数来推断要返回的具体类型的. 阅读全文 >>

Java 语言的几个缺陷之二: equals() 比较字符串

对于面向对象的语言不知道除了 Java 还有没别的语言会拿怎么比较两个字符串相等频频作为面试题来考. 原本是在编程语言中两个字符串内容是否相等时用 == 比较时却可能是不对的. 在 Java 中

"ab" == "ab"                                                                                   //true
"ab" == "new String("ab")                                                          //false
"ab" == String.value("ab")                                                         //true
new String("ab").equals(new String("ab"))                            //true
new String("ab").intern() == new String("ab").intern()      //true

在 Java 中明明看到两个字符串内容一样用 == 进行比较多数时候不是你想要的结果, 只有用 equals() 方法才是王道.  使用 Java 的字符串必须了解它内部是怎么存储的. 比于上面的结果我不作细说, 主要涉及到字符串常量池及内部状态, == 比较引用, equals() 比较内容.

Java 还常常对 equals 比较字符串津津乐道, 而我仍然认为它是语言设计上的一个缺陷, 所以 JVM 上的其他编程语言如 Groovy, Scala 纷纷倒勾, 无一不是用== 来比较字符串的内容, 它们也提供字符串引用的比较, 但多少人实际关心两个字符串的引用是否相同呢, 反正字符串设计的是 Immutable 的.

若说是因为 Java 不支持操作符的重载, 但可以像 Scala, Groovy 那样在编译器上下功夫的. 最终我想依然是受累于 100% 源代码与二进制的兼容性, 改进的话会造成早先代码的行为错乱. 阅读全文 >>

Java 语言的几个缺陷之一: 无 Here Document

Java  语言由于一直要保持源代码及二进制的向后兼容, 所以尽管语法上有了很大的演进, 但有些东西仍然无法触及.  不像有些新生代的语言设计时可以博采众长, 或者像 Scala 那种语言向后兼容性的要求没这么苛刻, Scala 只要保持主, 次版本相同时的兼容性. Java 则不同, 1.0 的代码或字节码放到 1.8 下还要能跑.

实际应用中在多个语言切换时, 很自然的会对其他语言与 Java 进行横向对比. 感觉 Java 语言有几个缺陷, 不在此篇中一一列举, 只把第一个不足之处道来.

Java 没有 Here Document 的支持, Here Document 又称 heredoc, hereis, here-string, here-script, 再通俗点讲就是多行字符串(multiline string). 在 Java 中如果要定义多行字符串, 需要连串的加号及换行符(\n), 如下

曾经为了寻求 Java 对 Here Document 的支持, 尝试过 Java 的  APT(Java Annotation Processing Tool), 定义注解 @HereDocument 使用注释, 编译时带 -processor 参数, 但是注释中的格式很容易被 IDE 给自动格式化掉, 见 Java 的多行字符串 Here Document 的实现. 所以这种尝试很快就放弃了, 因为项目中用 Java 和  Scala 混合编程, 真正需要 Here Document 的地方直接用 Scala 代码就行了. 阅读全文 >>

Clojure 快速突击(续)

Clojure 确实要比 Python 语义上庞大, 所以无法尽量在一篇之中收纳进来, 只得另立新篇, 也许还会有第三篇笔记. 现在开始学习函数定义

函数定义

函数用  defn 宏来定义, 函数名与参数列表之间可选的字符串是函数的注释, 相当于 Python 的函数体中第一个字符串,  这个字符串能被 (doc func-name)  列出来, Python 中是用 dir(fun_name 显示函数帮助. 不需要 return 关键字, 和 Groovy/Scala 一样最后一个表达式的值为函数的返回值, 所以函数总是有返回值(或为 nil).

和 C 语言一样, 函数必须先定义再使用, 否则要用 (declare function-names) 提前声明, 下面代码是在 Clojure 的 REPL 中执行的

提一下 Clojure 的命名规则, 变量和函数名用中划线连接的小写单词. defn- 定义的函数是私有, 只对当前名字空间可见, 比如上面的  user 名字空间. 有点像 Python 的下划线变量或函数的可见性约定. 阅读全文 >>

Clojure 快速突击

自己所学过的编程语言基本是 C 风格的, 给自己定下的目标是要学习下 Python, Swift 和 Clojure. 正如之前的 我的 Python 快速入门 那样的几分钟入门, 这里记录下 Clojure 的快速上手过程.

为什么是 Clojure, 因为它是 Lisp 的一个方言, 个人觉得有必要拓展一下不同的语言风格与思维方式, 就像当初接触 Objective-C 的 [person sayHello] 的方法调用有点不好理解一样, 其实把它还原为面向对象的本质是向 person 发送 sayHello 消息就简单了.

编程语方不仅仅是一种技术, 它更是一种思维习惯

希望通过 Clojure 这样的语言来感受另样的思维方式. Clojure 是运行在 JVM 之上的函数式 List 方言. Clojure 乍一看, 基本就是一个括号语言, 它的语法更能体现操作/函数为中心. Clojure 的圆括号兼具 C 风格语言的圆括号(参数列表), 分号(分隔语句), 以及大括号(限定作用域) 的功能. (1 + 2 + 3 +4) 只用写成 (+ 1 2 3 4)。

因为  Clojure 是构筑在 JVM 之上, 所以可以从 http://clojure.org/community/downloads 下载 clojure 的 jar 包, 然后

java -jar clojure-1.8.0.jar

就能进到 Clojure 的 REPL(read-eval-print-loop) 控制台了, 就可以开始体验 Clojure 的代码 user=> (+ 1 2 3) ,如果要运行一个已经写好的 Clojure 文件, 如 hello.clj, 就要用 java -jar clojure-1.8.0.jar hello.clj 来执行. 为方便可以建立一个脚本  clj, 内容为

java -jar /path/clojure.jar $1

阅读全文 >>

我的 Python 快速入门

本文只是我个人的掌握 Python 的快速入门笔记, 所以混乱不堪, 并不适合于每一个想要学习 Python 的读者

Python 命令进到它的 shell, ctrl+d 或 exit() 退出 python. help(str) 可以查看 str 函数的帮助, q 退出帮助. 对象的方法可用 dir 来查看, dir([]), dir(""), 进而 help([].append), help(dir([]))

Python 是用严格的缩进来格式化代码块的, Google 的 Python 代码规范是用 4 个空格来缩进. Google 建议 Java 是用两个空格.

Python 是动态类型的, 所以可以 a = 1; a = "string" 随意赋值为不同类型. Python 也能用分号把多条语句写在同一行里, 但基本没人用分号的.
Python 的基本类型有 整数, 长整数, 浮点数和复数, 以及字符串

字符串可以用单引号和双引号, 它们像 Javascript, 是完全一样的
''' 或 "”” 三引号的字符串是 here doc, 多行字符串
转义符也是用 \, 如 ''What\’s your name\n?
自然字符串: 即不转义, 用 R 或 r 来指定, 如 r"Newlines are indicated by \n”, 会输出 "\n"" 字面值. 可用于书写正则表达式
放在一起的字符串就会被 Python 自动连接, 如 print ''What\'s' ''your name?’, 输出为 "What’s your name?”

Python 的命名规则有几个必须知道的: 类名和 Java 一样; 模块, 方法, 变量名用小写字母下划线分隔, 常量用大写加下划线. 单或双下划线开头是特殊用途. 命名规则请参考 Google Python Style Guide#Naming
Python 是纯面向对象的, 任何东西都是对象, 函数也是

Python 可以用 \ 来连接语句行, 像 Bash 一样, 如 阅读全文 >>

Docker for Mac 公测版试用

伴随着 2016 年 Docker Conference 的进行, Docker for Mac 和 Windows 的版本终于完全公测开始了. 之前阅读 Docker for Mac Windows Beta: the simple way to use Docker on your laptop 里的介绍, 想要安装的必须在 https://beta.docker.com/  收到获准才能下载.

Docker for Mac and Windows 可能让我们在 Mac 或 Windows 下脱离 Virtual Box 虚拟机, 像在 Linux 下一样跑 Docker. 因为不存在了 Virtual Box 这一中间层, 端口重定向就变成了一件很简单的事情了. 再也不用像原来那样 Localhost:80 -> VirtualBox-Default:1080 -> Container:80, 而直接就是 Localhost:80 -> Container:80.

也不再需要用 eval $(docker-machine env default) 来设置 DOCKER_HOST 等环境变量了.

目前的 Beta 版是 Version 1.12.0-rc2-beta16 (build: 9493) f615be9fb245904fbdf1aa0cad251d418c869428

本人趁手的是 Mac, 所以大概讲述如何安装 Docker for Mac. 首先我们可以尽情的卸载掉已安装的 Virtual Box, Docker-Machine 和 Docker 了, 然后从 https://docs.docker.com/docker-for-mac/ 下载 Docker for Mac. 它是一个 dmg 文件, 想安装普通的 Mac 应用那样拖拽到 /Applications 目录,  阅读全文 >>

Vim 中标签(tab) 操作

Vim 自 7.0 开始支持 tab 页了,这就像很多数文本编辑器那样方便在多文件中切换,而不是只能使用 Buffer 暗地里来回切。默认时标签上显示 tab 序号加上当前打开的文件名。

注:Visual Studio Code 1.1.1 目前尚不支持 tab 功能。

Vim 中关于 Tab 的操作命令如下:

vim -p file1 file2 file3....   在多标签中打开多个文件

:tabe[dit] 或  :tabnew          在当前标签后打开新的标签

:tabn[ext] 或 gt         切换到下一个 tab

:tabp[revious] 或 gT   切换到上一个 tab

:tabn [N]    切换到第 N 个 tab

:tabfir[st]   切换到第 1 个 tab

:tabl[ast]    切换到最后一个 tab 阅读全文 >>

JMockit 如何 Mock 部分方法/属性

现在的 JMockit 已经偷偷升级到了 1.23 版了,在 JVM 上的 Mock 工具中就数它最无敌了,因为它抢夺了最佳控制点  --javaagent,可以说它是无所不能的。一般我们使用 JMockit 是通过两种方式,new MockUpnew Expectations. JMock 不仅能够 Mock 类的所有方法,还能部分 Mock -- 这个是 new Expectations 的默认行为。所以这里我们来看下在使用 new Expectations 的情况下如何对类的部份静态方法或部分实例方法进行 Mock。

大致表述一下,共分为三种情况

  1. 针对类进行 Mock, 只有录制的静态方法被 Mock 住,其他的静态方法或实例方法都会调用实际实现
  2. 针对某一实例进行 Mock,只在调用该实例已录制的方法才被 Mock 住,静态方法或新建实例调用任何方法都是实际实现
  3. 针对类进行 Mock,但录制的是一个实例方法,那么该实例或任何新建实例在调用该录制方法时都会被 Mock 住

不知道上面在说什么,本来就是空洞无凭,所以还是下实例,假定要测试下面这个类,或者说是测试使用到下面类的其他类 阅读全文 >>

sbt 最简单的带输入任务 inputTask

在 sbt 中我们可以定义 settingKey, taskKey 和 inputKey. inputKey 接收输入的任务更具灵活性,虽然在 sbt 中 taskKey 和  inputKey 数量比例为 25:1,但仍然不可忽视了 inputKey 的贡献。

起初在阅读 sbt 关于 inputKey 的资料时,一不小心就被带入到 Parser 上去了。其实还不如开门见山,先跳过 Parser 部份,示范 inputKey 任务中直接处理用户的原生输入。

这里有两个最简单的 sbt inputKey 的示例

一. 命令后非空格起全部输入当作一个字符串

trimmed() 方法能去除了两边的空格,如果是 token(any.* map(_.mkString)).parse 则两边空格都会算上,在 demo1 abc  的 abc 两边的空格也都会算在输入参数里

> demo1 abc "abc"
abc "abc"


sbt  的  input task  参数居然默认是紧接着命令开始的,如上面如果输入是 demo1xxx, demo1 任务接收到的参数是 xxx. 如果同时也定义了另一个任务是 demo1x, 那么输入 demo1xx 就会执行 demo1x 任务,参数是 xx. 从后往前进行匹配任务名。这个还有点奇怪,因为我以前任何时候都是自然的把命令与参数用空格分开,它却可以把命令与参数写在一起。

 


二. 像调用 main 方法一样传入参数

阅读全文 >>