经过几天的Scala回归Java的10个最烦人的事情

因此,我正在尝试使用Scala,因为我想编写一个解析器,而Scala Parsers API似乎非常合适。 毕竟,我可以在Scala中实现解析器并将其包装在Java接口后面,因此除了附加的运行时依赖关系之外,不应该存在任何互操作性问题。

在几天后真正真正地习惯了Scala语法的强大功能之后,以下是我回到编写Java时最想错过的十件事:

1.多行字符串

这是我个人的最爱,也是一种非常棒的功能,应该使用任何语言。 甚至PHP都有:多行字符串。 就像写作一样简单:

println ("""Dear reader,

If we had this feature in Java,
wouldn't that be great?

Yours Sincerely,
Lukas""")

这在哪里有用? 使用SQL,当然! 这是使用jOOQ和Scala运行纯SQL语句的方法:

println(
  DSL.using(configuration)
     .fetch("""
            SELECT a.first_name, a.last_name, b.title
            FROM author a
            JOIN book b ON a.id = b.author_id
            ORDER BY a.id, b.id
            """)
)

这不仅适用于静态字符串。 使用字符串插值,您可以轻松地将变量注入到这样的字符串中:

val predicate =
  if (someCondition)
    "AND a.id = 1"
  else
    ""

println(
  DSL.using(configuration)
      // Observe this little "s"
     .fetch(s"""
            SELECT a.first_name, a.last_name, b.title
            FROM author a
            JOIN book b ON a.id = b.author_id
            -- This predicate is the referencing the
            -- above Scala local variable. Neat!
            WHERE 1 = 1 $predicate
            ORDER BY a.id, b.id
            """)
)

太棒了,不是吗? 对于SQL,Scala具有很大的潜力。

jooq在scala-smalll中编写SQL的最佳方法

2.分号

我真心没有错过他们一点。 我构造代码的方式(可能也是大多数人构造代码的方式),Scala似乎根本不需要分号。 在JavaScript中,我不会说同样的话。 JavaScript的解释性和非类型安全性质似乎表明,放弃可选的语法元素可以保证您一臂之力。 但是Scala不支持。

val a = thisIs.soMuchBetter()
val b = no.semiColons()
val c = at.theEndOfALine()

可能是由于Scala的类型安全性导致的,这将使编译器在那些罕见的模棱两可的情况下抱怨,但这只是有根据的猜测。

3.括号

这是一个雷区,在许多情况下,省略括号似乎很危险。 实际上,在调用方法时,您也可以忽略点:

myObject method myArgument

由于这种方法会产生大量歧义,尤其是在链接更多的方法调用时,我认为最好避免这种技术。 但是在某些情况下,“忘记”父母很方便。 例如

val s = myObject.toString

4.类型推断

在Java中,这真的很烦人,与此同时,似乎还有许多其他语言也做到了这一点。 Java仅具有有限的类型推断功能,而且事情并不尽如人意

在Scala中,我可以简单地写:

val s = myObject.toString

……而不关心s是String类型的事实。 有时,但仅在某些情况下,我希望明确指定引用的类型。 在那种情况下,我仍然可以做到:

val s : String = myObject.toString

5.案例分类

我想我想写另一个POJO,它具有40个属性,构造函数,getter,setter,equals,hashCode和toString

-没人说。 曾经

Scala具有案例类。 用单线编写的简单不可变的pojos。 以Person案例类为例:

case class Person(firstName: String, lastName: String)

我同意,必须确实写下一次属性。 但是其他一切应该是自动的。

以及如何创建此类案例类的实例? 轻松,你甚至都不需要new运营商(事实上,它完全逃脱了我的想象,为什么new真正需要摆在首位):

Person("George", "Orwell")

而已。 您还想写些什么以符合企业标准?

边注

好的,现在有些人会争辩说要使用lombok项目 。 基于注释的代码生成是胡说八道,应最好避免。 实际上,Java生态系统中的许多注释简单地证明了Java语言的(而且将永远是)其演化能力非常有限这一事实。 以@Override为例。 这应该是关键字,而不是注释。 您可能会认为这是表面上的区别,但是我说Scala已经证明注释几乎总是错误的工具。 还是您最近看过带有大量注释的 Scala代码?

6.到处都有方法(功能!)

我认为,这实际上是任何语言中最有用的功能之一。 为什么我们总是必须将方法链接到特定的类? 为什么我们不能简单地拥有任何作用域级别的方法? 因为我们可以,所以使用Scala:

// "Top-level", i.e. associated with the package
def m1(i : Int) = i + 1

object Test {

    // "Static" method in the Test instance
    def m2(i : Int) = i + 2
    
    def main(args: Array[String]): Unit = {

        // Local method in the main method
        def m3(i : Int) = i + 3
        
        println(m1(1))
        println(m2(1))
        println(m3(1))
    }
}

对? 为什么我不能在另一个方法中定义本地方法? 我可以使用Java中的类来做到这一点:

public void method() {
    class LocalClass {}

    System.out.println(new LocalClass());
}

局部类是方法局部的内部类。 这几乎没有用,但是真正有用的是局部方法。

JavaScript或REPL也支持这些功能。 这对于在应用程序范围之外测试小型算法或概念非常有用。

在Java中,我们通常倾向于这样做:

public class SomeRandomClass {

    // [...]
  
    public static void main(String[] args) {
        System.out.println(SomeOtherClass.testMethod());
    }

    // [...]
}

在Scala中,我将在REPL中编写以下代码:

println(SomeOtherClass.testMethod)

还请注意始终可用的println方法。 纯金方面的高效调试。

8.数组不是(很多)特例

在Java中,除了基本类型以外,还有一些我们称为数组的怪异事物。 数组起源于一个完全独立的宇宙,在这里我们必须记住起源于柯克上尉(大约)时代的古怪规则:

数组

是的,规则如下:

// Compiles but fails at runtime
Object[] arrrrr = new String[1];
arrrrr[0] = new Object();

// This works
Object[] arrrr2 = new Integer[1];
arrrr2[0] = 1; // Autoboxing

// This doesn't work
Object[] arrrr3 = new int[];

// This works
Object[] arr4[] = new Object[1][];

// So does this (initialisation):
Object[][] arr5 = { { } };

// Or this (puzzle: Why does it work?):
Object[][] arr6 = { { new int[1] } };

// But this doesn't work (assignment)
arr5 = { { } };

是的,清单可以继续。 从句法上讲,使用Scala,数组不再是一种特殊情况:

val a = new Array[String](3);
a(0) = "A"
a(1) = "B"
a(2) = "C"
a.map(v => v + ":")

// output Array(A:, B:, C:)

如您所见,数组的行为与其他集合非常相似,包括可以在其上使用的所有有用方法。

9.符号方法名称

现在,这个话题更具争议性,因为它使我们想起了运算符重载危险 。 但是,每隔一段时间,我们希望有类似的东西。 可以让我们写的东西:

val x = BigDecimal(3);
val y = BigDecimal(4);
val z = x * y

非常直观地,z的值应为BigDecimal(12) 。 那不能太难了,可以吗? 我不在乎*的实现是否真的是一个称为multiply()的方法。 写下该方法时,我想使用看起来很普通的运算符进行乘法。

顺便说一句,我也想用SQL做到这一点。 这是一个例子:

select ( 
  AUTHOR.FIRST_NAME || " " || AUTHOR.LAST_NAME,
  AUTHOR.AGE - 10
)
from AUTHOR
where AUTHOR.ID > 10
fetch

那没有道理吗? 我们知道|| 表示concat(在某些数据库中)。 我们知道- (减号)和> (大于)的含义。 为什么不写呢?

上面是在Scala中jOOQ的编译示例。

jooq最好的方式在scala中写sql小

注意:警告

允许操作符重载或符号方法名之类的东西总是存在缺点。 它可能(并将被)滥用。 图书馆与Scala语言本身一样多

10.元组

作为一个SQL人员,这再次是我在其他语言中最想念的功能之一。 在SQL中,所有内容都是TABLE或ROW。 实际上,很少有人知道这一点 ,并且很少有数据库实际上支持这种思维方式。

Scala没有ROW类型(实际上是记录),但是至少有匿名元组类型。 将行视为具有命名属性的元组,而案例类将命名为行:

  • 元组:具有类型化和索引元素的匿名类型
  • 行:具有类型化,命名和索引元素的匿名类型
  • 案例类:带有类型化元素和命名元素的命名类型

在Scala中,我可以这样写:

// A tuple with two values
val t1 = (1, "A")

// A nested tuple
val t2 = (1, "A", (2, "B"))

在Java中,可以完成类似的操作,但是您必须自己编写该库,并且不提供语言支持:

class Tuple2<T1, T2> {
    // Lots of bloat, see missing case classes
}

class Tuple3<T1, T2, T3> {
    // Bloat bloat bloat
}

然后:

// Yikes, no type inference...
Tuple2<Integer, String> t1 = new Tuple2<>(1, "A");

// OK, this will certainly not look nice
Tuple3<Integer, String, Tuple2<Integer, String>> t2 =
    new Tuple3<>(1, "A", new Tuple2<>(2, "B"));

jOOQ充分利用了上述技术,将SQL的行值表达式带到Java,而且令人惊讶的是,在大多数情况下,您可以在不丢失类型推断的情况下做到这一点,因为jOOQ是一种流利的API,您从未真正将值分配给局部变量。例:

DSL.using(configuration)
   .select(T1.SOME_VALUE)
   .from(T1)
   .where(
      // This ROW constructor is completely type safe
      row(T1.COL1, T1.COL2)
      .in(select(T2.A, T2.B).from(T2))
   )
   .fetch();

结论

当然,这是一篇有关scala的文章,与Java稍有抵触。 不要误会我的意思。 我绝不希望完全迁移到Scala。 我认为Scala语言远远超出了任何有用软件中的合理范围。 有许多看起来不错的小功能和头,但不可避免地会炸掉您的脸,例如:

  • implicit转换。 这不仅很难管理,而且还严重降低了编译速度。 此外,使用implicit合理地实现语义版本控制可能完全是不可能的,因为不可能通过偶然的向后不兼容预见所有可能的客户端代码损坏。
  • 本地导入乍一看似乎很棒,但是当人们开始部分导入或重命名本地范围的类型时,它们的功能很快使代码难以理解。
  • 符号方法名称最常被滥用。 以解析器API为例,它具有诸如^^^^^^?等方法名称^?~!

尽管如此,我认为本文中列出的Scala与Java相比的优点也可以全部用Java实现:

  • 几乎没有破坏向后兼容的风险
  • 用(可能)不太大的努力,在JLS方面
  • 对开发人员的生产力产生巨大影响
  • 对Java的竞争力产生巨大影响

无论如何,Java 9将是另一个有前途的版本,其热门话题包括值类型, 声明站点差异特殊化(非常有趣!)ClassDynamic。

有了这些巨大的变化,我们希望上面的一些小改进还有一定的余地,这将为日常工作增加更多的直接价值。

翻译自: https://www.javacodegeeks.com/2014/08/the-10-most-annoying-things-coming-back-to-java-after-some-days-of-scala.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值