Java 语言的几个缺陷之六: 无字符串插值

Java 在定义字符串的时候不支持字符串插值, 即不能在字符串中捕获作用域中的变量, 用来组成当前字符串. 而这可以说是其他各种言都具备的基本特性. 例如 Bash 中可以这样

bash-3.2$ name=Yanbin
bash-3.2$ echo "Hello $name"
Hello Yanbin

Java 中不支持类似的方式

String name = "Yanbin";
String greeting = "Hello $name";  //Java 中无法把 $name 替换为 name 变量值 "Yanbin"

而使用 Java 必须用 String 的静态方法 format (注意是静态方法哦) 来间接的格式化出一个字符串, 可以这么写

String.format("Hello %s", "World");
String.format("%1$s %1$s %s %s %2$s", "aa", 10);    //aa aa aa 10 10

我们看到在用 format 格式化时匹配参数会是一个不小的问题, 相同使用法的打印输出方法是 System.out.printf(). 别说字符串插值了, 如果 format 方法是 String 的一个实例方法应用起来都会便利些, 如 "Hello %s".format("World") 会容易读些, Scala 的字符串实例就有 format 方法了. 阅读全文 >>

Java 语言的几个缺陷之五: 多返回值问题

希望 Java 能支持动态对象(匿名对象) 的特性是源于想要 Java 方法能优雅的返回多个值. 目前如果希望 Java 方法返回多个值的做法有返回一个自定义对象, 数组或列表, 或 Map. 这种需求多发生在私有方法上, 但目前的解决办法有如下弊端:

  1. 如果用自定义类来作为返回类型的话, 会使得类过于杂乱, 而且这些自定义类的复用性不高
  2. 数组或列表有太强的顺序依赖, 没有属性名告知每个位置上值的意义, 而且类型都必须为 Object
  3. 返回 Map 的, 在维护 Key 上容易出错(或须为 Key 定义很多字符串常量), 且类型也是为 Object.

说了上面许多, 各位可能还不定清楚我想要的是什么, 其实就是类似于 C# 的匿名对象(或曰动态对象), 看一段 C#  的方法返回一个匿名对象的例子(.NET3.5 开始引入匿名对象, .NET 4.0 后匿名对象可以作为函数值) 阅读全文 >>

Java 语言的几个缺陷之四: 过时的 JavaBean

曾几何时在业务分层结构中的 VO 或 DTO 层充斥着无数的标准 JavaBean 类, 那些碍手脚的 getter/setter 方法简值不忍直视. 或许 JavaBean 设定规范的用意是当某些属性为只读时不提供 setter 方法, 而实际使用时, 因 getter/setter 都同时具备, 那么 JavaBean 的所有私有属性又何异于公有属性呢. 

更别说对于某些形式的属性名, 若属性名为 xCoordinate  时, 它所对应的 getter 方法分别是 getxCoordinate(),一般的 IDE 都会为它自动生成 getXCoordinate() 方法, 这是错误的.  实际上 getXCoordinate() 对应的属性名是 XCoordinate

所以 Play Framework 1 以及 Play Framework 2.4.6 之前的版本采用了字节码增强的技术, 实现了像 Objective-C 的 @property 的特性, 即只要声明公有属性, 编译器为该属性生成默认的 getter/setter 方法, 您也可以手工去覆盖个别默认的 getter/setter 方法.

因此在 Play Framework 中书写的的  model 类就只需要属性了, 像

就这么简单, 想像一下如果我们为一个众多属性的 model 类补全所有的 getter/setter 方法读起来有多恐怖. 阅读全文 >>

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 代码就行了. 阅读全文 >>