文档章节

挑逗 Java 程序员的那些 Scala 绝技

joymufeng
 joymufeng
发布于 10/23 10:10
字数 5749
阅读 3813
收藏 17

有个问题一直困扰着 Scala 社区,为什么一些 Java 开发者将 Scala 捧到了天上,认为它是来自上帝之吻的完美语言;而另外一些 Java 开发者却对它望而却步,认为它过于复杂而难以理解。同样是 Java 开发者,为何会出现两种截然不同的态度,我想这其中一定有误会。Scala 是一粒金子,但是被一些表面上看起来非常复杂的概念或语法包裹的太严实,以至于人们很难在短时间内搞清楚它的价值。与此同时,Java 也在不断地摸索前进,但是由于 Java 背负了沉重的历史包袱,所以每向前一步都显得异常艰难。本文主要面向 Java 开发人员,希望从解决 Java 中实际存在的问题出发,梳理最容易吸引 Java 开发者的一些 Scala 特性。希望可以帮助大家快速找到那些真正可以打动你的点。

类型推断

挑逗指数: 四星

我们知道,Scala 一向以强大的类型推断闻名于世。很多时候,我们无须关心 Scala 类型推断系统的存在,因为很多时候它推断的结果跟直觉是一致的。 Java 在 2016 年也新增了一份提议JEP 286,计划为 Java 10 引入局部变量类型推断(Local-Variable Type Inference)。利用这个特性,我们可以使用 var 定义变量而无需显式声明其类型。很多人认为这是一项激动人心的特性,但是高兴之前我们要先看看它会为我们带来哪些问题。

与 Java 7 的钻石操作符冲突

Java 7 引进了钻石操作符,使得我们可以降低表达式右侧的冗余类型信息,例如:

List<Integer> numbers = new ArrayList<>();

如果引入了 var,则会导致左侧的类型丢失,从而导致整个表达式的类型丢失:

var numbers = new ArrayList<>();

所以 var 和 钻石操作符必须二选一,鱼与熊掌不可兼得。

容易导致错误的代码

下面是一段检查用户是否存在的 Java 代码:

public boolean userExistsIn(Set<Long> userIds) {
    var userId = getCurrentUserId();
    return userIds.contains(userId);
}

请仔细观察上述代码,你能一眼看出问题所在吗? userId 的类型被 var 隐去了,如果 getCurrentUserId() 返回的是 String 类型,上述代码仍然可以正常通过编译,却无形中埋下了隐患,这个方法将会永远返回 false, 因为 Set<Long>.contains 方法接受的参数类型是 Object。可能有人会说,就算显式声明了类型,不也是于事无补吗?

public boolean userExistsIn(Set<Long> userIds) {
    String userId = getCurrentUserId();
    return userIds.contains(userId);
}

Java 的优势在于它的类型可读性,如果显式声明了 userId 的类型,虽然还是可以正常通过编译,但是在代码审查时,这个错误将会更容易被发现。 这种类型的错误在 Java 中非常容易发生,因为 getCurrentUserId() 方法很可能因为重构而改变了返回类型,而 Java 编译器却在关键时刻背叛了你,没有报告任何的编译错误。 虽然这是由于 Java 的历史原因导致的,但是由于 var 的引入,会导致这个错误不断的蔓延。

很显然,在 Scala 中,这种低级错误是无法逃过编译器法眼的:

def userExistsIn(userIds: Set[Long]): Boolean = {
    val userId = getCurrentUserId()
    userIds.contains(userId)
}

如果 userId 不是 Long 类型,则上面的程序无法通过编译。

字符串增强

挑逗指数: 四星

常用操作

Scala 针对字符作进行了增强,提供了更多的使用操作:

//字符串去重
"aabbcc".distinct // "abc"

//取前n个字符,如果n大于字符串长度返回原字符串
"abcd".take(10) // "abcd"

//字符串排序
"bcad".sorted // "abcd"

//过滤特定字符
"bcad".filter(_ != 'a') // "bcd"

//类型转换
"true".toBoolean
"123".toInt
"123.0".toDouble

其实你完全可以把 String 当做 Seq[Char] 使用,利用 Scala 强大的集合操作,你可以随心所欲地操作字符串。

原生字符串

在 Scala 中,我们可以直接书写原生字符串而不用进行转义,将字符串内容放入一对三引号内即可:

//包含换行的字符串
val s1= """Welcome here.
   Type "HELP" for help!"""
   
//包含正则表达式的字符串   
val regex = """\d+"""   

字符串插值

通过 s 表达式,我们可以很方便地在字符串内插值:

val name = "world"
val msg = s"hello, ${name}" // hello, world

集合操作

挑逗指数: 五星

Scala 的集合设计是最容易让人着迷的地方,就像毒品一样,一沾上便让人深陷其中难以自拔。通过 Scala 提供的集合操作,我们基本上可以实现 SQL 的全部功能,这也是为什么 Scala 能够在大数据领域独领风骚的重要原因之一。

简洁的初始化方式

在 Scala 中,我们可以这样初始化一个列表:

val list1 = List(1, 2, 3)

可以这样初始化一个 Map:

val map = Map("a" -> 1, "b" -> 2)

所有的集合类型均可以用类似的方式完成初始化,简洁而富有表达力。

便捷的 Tuple 类型

有时方法的返回值可能不止一个,Scala 提供了 Tuple (元组)类型用于临时存放多个不同类型的值,同时能够保证类型安全性。千万不要认为使用 Java 的 Array 类型也可以同样实现 Tuple 类型的功能,它们之间有着本质的区别。Tuple 会显式声明所有元素的各自类型,而不是像 Java Array 那样,元素类型会被向上转型为所有元素的父类型。
我们可以这样初始化一个 Tuple:

val t = ("abc", 123, true)
val s: String  = t._1 // 取第1个元素
val i: Int     = t._2 // 取第2个元素
val b: Boolean = t._3 // 取第3个元素

需要注意的是 Tuple 的元素索引从1开始。

下面的示例代码是在一个长整型列表中寻找最大值,并返回这个最大值以及它所在的位置:

def max(list: List[Long]): (Long, Int) = list.zipWithIndex.sorted.reverse.head

我们通过 zipWithIndex 方法获取每个元素的索引号,从而将 List[Long] 转换成了 List[(Long, Int)],然后对其依次进行排序、倒序和取首元素,最终返回最大值及其所在位置。

链式调用

通过链式调用,我们可以将关注点放在数据的处理和转换上,而无需考虑如何存储和传递数据,同时也避免了创建大量无意义的中间变量,大大增强程序的可读性。其实上面的 max 函数已经演示了链式调用。下面我们演示一下如何使用集合操作实现 SQL 的关联查询功能,待实现的 SQL 语句如下:

SELECT p.name, p.company, c.country FROM people p JOIN companies c ON p.companyId = c.id
WHERE p.age == 20

上面 SQL 语句实现的功能是关联查询 people 和 companies 两张表,返回年龄为20岁的所有员工名称、年龄以及其所在公司名称。

对应的 Scala 实现代码如下:

// Entity
case class People(name: String, age: Int, companyId: String)
case class Company(id: String, name: String)

// Entity List
val people    = List(People("jack", 20, "0"))
val companies = List(Company("0", "lightbend"))

// 实现关联查询
people
  .filter(p => p.age == 20)
  .flatMap{ p =>
    companies
      .filter(c => c.id == p.companyId)
      .map(c => (p.name, p.age, c.name))
}
//结果:List((jack,20,lightbend))

其实使用 for 表达式看起来更加简洁:

for {
  p <- people if p.age == 20
  c <- companies if p.companyId == c.id
} yield (p.name, p.age, c.name)

非典型集合操作

Scala 的集合操作非常丰富,如果要详细说明足够写一本书了。这里仅列出一些不那么常用但却非常好用的操作。

去重:

List(1, 2, 2, 3).distinct // List(1, 2, 3)

交集:

Set(1, 2) & Set(2, 3)   // Set(2)

并集:

Set(1, 2) | Set(2, 3) // Set(1, 2, 3)

差集:

Set(1, 2) &~ Set(2, 3) // Set(1)

排列:

List(1, 2, 3).permutations.toList
//List(List(1, 2, 3), List(1, 3, 2), List(2, 1, 3), List(2, 3, 1), List(3, 1, 2), List(3, 2, 1))

组合:

List(1, 2, 3).combinations(2).toList 
// List(List(1, 2), List(1, 3), List(2, 3))

并行集合

Scala 的并行集合可以利用多核优势加速计算过程,通过集合上的 par 方法,我们可以将原集合转换成并行集合。并行集合利用分治算法将计算任务分解成很多子任务,然后交给不同的线程执行,最后将计算结果进行汇总。下面是一个简单的示例:

(1 to 10000).par.filter(i => i % 2 == 1).sum

优雅的值对象

挑逗指数: 五星

Case Class

Scala 标准库包含了一个特殊的 Class 叫做 Case Class,专门用于领域层值对象的建模。它的好处是所有的默认行为都经过了合理的设计,开箱即用。下面我们使用 Case Class 定义了一个 User 值对象:

case class User(name: String, role: String = "user", addTime: Instant = Instant.now())

仅仅一行代码便完成了 User 类的定义,请脑补一下 Java 的实现。

简洁的实例化方式

我们为 role 和 addTime 两个属性定义了默认值,所以我们可以只使用 name 创建一个 User 实例:

val u = User("jack")

在创建实例时,我们也可以命名参数(named parameter)语法改变默认值:

val u = User("jack", role = "admin")

在实际开发中,一个模型类或值对象可能拥有很多属性,其实很多属性都可以设置一个合理的默认值。利用默认值和命名参数,我们可以非常方便地创建模型类和值对象的实例。 所以在 Scala 中基本上不需要使用工厂模式或构造器模式创建对象,如果对象的创建过程确实非常复杂,则可以放在伴生对象中创建,例如:

object User {
  def apply(name: String): User = User(name, "user", Instant.now())
}

在使用伴生对象方法创建实例时可以省略方法名 apply,例如:

User("jack") // 等价于 User.apply("jack")

在这个例子里,使用伴生对象方法实例化对象的代码,与上面使用类构造器的代码完全一样,编译器会优先选择伴生对象的 apply 方法。

不可变性

Case Class 在默认情况下实例是不可变的,意味着它可以被任意共享,并发访问时也无需同步,大大地节省了宝贵的内存空间。而在 Java 中,对象被共享时需要进行深拷贝,否则一个地方的修改会影响到其它地方。例如在 Java 中定义了一个 Role 对象:

public class Role {
    public String id = "";
    public String name = "user";
    
    public Role(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

如果在两个 User 之间共享 Role 实例就会出现问题,就像下面这样:

u1.role = new Role("user", "user");
u2.role = u1.role;

当我们修改 u1.role 时,u2 就会受到影响,Java 的解决方式是要么基于 u1.role 深度克隆一个新对象出来,要么新创建一个 Role 对象赋值给 u2。

对象拷贝

在 Scala 中,既然 Case Class 是不可变的,那么如果想改变它的值该怎么办呢?其实很简单,利用命名参数可以很容易拷贝一个新的不可变对象出来:

val u1 = User("jack")
val u2 = u1.copy(name = "role", role = "admin")

清晰的调试信息

我们不需要编写额外的代码便可以得到清晰的调试信息,例如:

val users = List(User("jack"), User("rose"))
println(users)

输出内容如下:

List(User(jack,user,2018-10-20T13:03:16.170Z), User(rose,user,2018-10-20T13:03:16.170Z))

默认使用值比较相等性

在 Scala 中,默认采用值比较而非引用比较,使用起来更加符合直觉:

User("jack") == User("jack") // true

上面的值比较是开箱即用的,无需重写 hashCode 和 equals 方法。

模式匹配

挑逗指数: 五星

更强的可读性

当你的代码中存在多个 if 分支并且 if 之间还会有嵌套,那么代码的可读性将会大大降低。而在 Scala 中使用模式匹配可以很容易地解决这个问题,下面的代码演示货币类型的匹配:

sealed trait Currency
case class Dollar(value: Double) extends Currency
case class Euro(value: Double) extends Currency
val Currency = ...
currency match {
    case Dollar(v) => "$" + v
    case Euro(v) => "€" + v
    case _ => "unknown"
}

我们也可以进行一些复杂的匹配,并且在匹配时可以增加 if 判断:

use match {
    case User("jack", _, _) => ...
    case User(_, _, addTime) if addTime.isAfter(time) => ...
    case _ => ...
}

变量赋值

利用模式匹配,我们可以快速提取特定部分的值并完成变量定义。 我们可以将 Tuple 中的值直接赋值给变量:

val tuple = ("jack", "user", Instant.now())
val (name, role, addTime) = tuple
// 变量 name, role, addTime 在当前作用域内可以直接使用

对于 Case Class 也是一样:

val User(name, role, addTime) = User("jack")
// 变量 name, role, addTime 在当前作用域内可以直接使用

并发编程

挑逗指数: 五星

在 Scala 中,我们在编写并发代码时只需要关心业务逻辑即可,而不需要关注任务如何执行。我们可以通过显式或隐式方式传入一个线程池,具体的执行过程由线程池完成。Future 用于启动一个异步任务并且保存执行结果,我们可以用 for 表达式收集多个 Future 的执行结果,从而避免回调地狱:

val f1 = Future{ 1 + 2 }
val f2 = Future{ 3 + 4 }
for {
    v1 <- f1
    v2 <- f2
}{
    println(v1 + v2) // 10
}

使用 Future 开发爬虫程序将会让你事半功倍,假如你想同时抓取 100 个页面数据,一行代码就可以了:

Future.sequence(urls.map(url => http.get(url))).foreach{ contents => ...}

Future.sequence 方法用于收集所有 Future 的执行结果,通过 foreach 方法我们可以取出收集结果并进行后续处理。

当我们要实现完全异步的请求限流时,就需要精细地控制每个 Future 的执行时机。也就是说我们需要一个控制Future的开关,没错,这个开关就是Promise。每个Promise实例都会有一个唯一的Future与之相关联:

val p = Promise[Int]()
val f = p.future
for (v <- f) { println(v) } // 3秒后才会执行打印操作

//3秒钟之后返回3
Thread.sleep(3000)
p.success(3)

跨线程错误处理

Java 通过异常机制处理错误,但是问题在于 Java 代码只能捕获当前线程的异常,而无法跨线程捕获异常。而在 Scala 中,我们可以通过 Future 捕获任意线程中发生的异常。
异步任务可能成功也可能失败,所以我们需要一种既可以表示成功,也可以表示失败的数据类型,在 Scala 中它就是 Try[T]。Try[T] 有两个子类型,Success[T]表示成功,Failure[T]表示失败。就像量子物理学中薛定谔的猫,在异步任务执行之前,你根本无法预知返回的结果是 Success[T] 还是 Failure[T],只有当异步任务完成执行以后结果才能确定下来。

val f = Future{ /*异步任务*/ } 

// 当异步任务执行完成时
f.value.get match {
  case Success(v) => // 处理成功情况
  case Failure(t) => // 处理失败情况
}

我们也可以让一个 Future 从错误中恢复:

val f = Future{ /*异步任务*/ }
for{
  result <- f.recover{ case t => /*处理错误*/ }
} yield {
  // 处理结果
}

声明式编程

挑逗指数: 四星

Scala 鼓励声明式编程,采用声明式编写的代码可读性更强。与传统的过程式编程相比,声明式编程更关注我想做什么而不是怎么去做。例如我们经常要实现分页操作,每页返回 10 条数据:

val allUsers = List(User("jack"), User("rose"))
val pageList = 
  allUsers
    .sortBy(u => (u.role, u.name, u.addTime)) // 依次按 role, name, addTime 进行排序
    .drop(page * 10) // 跳过之前页数据
    .take(10) // 取当前页数据,如不足10个则全部返回

你只需要告诉 Scala 要做什么,比如说先按 role 排序,如果 role 相同则按 name 排序,如果 role 和 name 都相同,再按 addTime 排序。底层具体的排序实现已经封装好了,开发者无需实现。

面向表达式编程

挑逗指数: 四星

在 Scala 中,一切都是表达式,包括 if, for, while 等常见的控制结构均是表达式。表达式和语句的不同之处在于每个表达式都有明确的返回值。

val i = if(true){ 1 } else { 0 } // i = 1
val list1 = List(1, 2, 3)
val list2 = for(i <- list1) yield { i + 1 }

不同的表达式可以组合在一起形成一个更大的表达式,再结合上模式匹配将会发挥巨大的威力。下面我们以一个计算加法的解释器来做说明。

一个整数加法解释器

我们首先定义基本的表达式类型:

abstract class Expr
case class Number(num: Int) extends Expr
case class PlusExpr(left: Expr, right: Expr) extends Expr

上面定义了两个表达式类型,Number 表示一个整数表达式, PlusExpr 表示一个加法表达式。
下面我们基于模式匹配实现表达式的求值运算:

def evalExpr(expr: Expr): Int = {
  expr match {
    case Number(n) => n
    case PlusExpr(left, right) => evalExpr(left) + evalExpr(right)
  }
}

我们来尝试针对一个较大的表达式进行求值:

evalExpr(PlusExpr(PlusExpr(Number(1), Number(2)), PlusExpr(Number(3), Number(4)))) // 10

隐式参数和隐式转换

挑逗指数: 五星

隐式参数

如果每当要执行异步任务时,都需要显式传入线程池参数,你会不会觉得很烦?Scala 通过隐式参数为你解除这个烦恼。例如 Future 在创建异步任务时就声明了一个 ExecutionContext 类型的隐式参数,编译器会自动在当前作用域内寻找合适的 ExecutionContext,如果找不到则会报编译错误:

implicit val ec: ExecutionContext = ???
val f = Future { /*异步任务*/ }

当然我们也可以显式传递 ExecutionContext 参数,明确指定使用的线程池:

implicit val ec: ExecutionContext = ???
val f = Future { /*异步任务*/ }(ec)

隐式转换

隐式转换相比较于隐式参数,使用起来更来灵活。如果 Scala 在编译时发现了错误,在报错之前,会先对错误代码应用隐式转换规则,如果在应用规则之后可以使得其通过编译,则表示成功地完成了一次隐式转换。

在不同的库间实现无缝对接

当传入的参数类型和目标类型不匹配时,编译器会尝试隐式转换。利用这个功能,我们将已有的数据类型无缝对接到三方库上。例如我们想在 Scala 项目中使用 MongoDB 的官方 Java 驱动执行数据库查询操作,但是查询接口接受的参数类型是 BsonDocument,由于使用 BsonDocument 构建查询比较笨拙,我们希望能够使用 Scala 的 JSON 库构建一个查询对象,然后直接传递给官方驱动的查询接口,而无需改变官方驱动的任何代码,利用隐式转换可以非常轻松地实现这个功能:

implicit def toBson(json: JsObject): BsonDocument =  ...

val json: JsObject = Json.obj("_id" -> "0")
jCollection.find(json) // 编译器会自动调用 toBson(json)

利用隐式转换,我们可以在不改动三方库代码的情况下,将我们的数据类型与其进行无缝对接。例如我们通过实现一个隐式转换,将 Scala 的 JsObject 类型无缝地对接到了 MongoDB 的官方 Java 驱动的查询接口中,看起就像是 MongoDB 官方驱动真的提供了这个接口一样。

同时我们也可以将来自三方库的数据类型无缝集成到现有的接口中,也只需要实现一个隐式转换方法即可。

扩展已有类的功能

例如我们定义了一个美元货币类型 Dollar:

class Dollar(value: Double) {
  def + (that: Dollar): Dollar = ...
  def + (that: Int): Dollar = ...
}

于是我们可以执行如下操作:

val halfDollar = new Dollar(0.5)
halfDollar + halfDollar // 1 dollar
halfDollar + 0.5 // 1 dollar

但是我们却无法执行像 0.5 + halfDollar 这样的运算,因为在 Double 类型上无法找到一个合适的 + 方法。

在 Scala 中,为了实现上面的运算,我们只需要实现一个简单的隐式转换就可以了:

implicit def doubleToDollar(d: Double) = new Dollar(d)

0.5 + halfDollar // 等价于 doubleToDollar(0.5) + halfDollar

更好的运行时性能

在日常开发中,我们通常需要将值对象转换成 Json 格式以方便数据传输。Java 的通常做法是使用反射,但是我们知道使用反射是要付出代价的,要承受运行时的性能开销。而 Scala 则可以在编译时为值对象生成隐式的 Json 编解码器,这些编解码器只不过是普通的函数调用而已,不涉及任何反射操作,在很大程度上提升了系统的运行时性能。

小结

如果你坚持读到了这里,我会觉得非常欣慰,很大可能上 Scala 的某些特性已经吸引了你。但是 Scala 的魅力远不止如此,以上列举的仅仅是一些最容易抓住你眼球的一些特性。如果你愿意推开 Scala 这扇大门,你将会看到一个完全不一样的编程世界。本文欢迎转载,请注明作者沐风(joymufeng)。

后记
首先感谢大家关注本文,也非常感谢 @dwingo 参与讨论。Scala 和 Java 同根同源,并且完全拥抱现有 Java 生态,在开发中我们也经常使用两种语言混合编程,所以 Scala = Java and More。全文都是围绕 Scala 的"简洁性"展开,Scala 在保持简洁性的同时,提供了非常好的可读性,可以参考本文的"声明式编程"一节。Scala的很多设计都是对 Java 的改良与超越,所以学习 Scala 的过程其实是一次对 Java 的深度回顾。在这里向大家推荐一本书《快学Scala 第二版》(高宇翔 译),作者 Cay S. Horstmann 是一位 Java 语言大师,并且是《Java核心技术》卷1和卷2的作者,全书围绕Java与Scala展开,行文简洁通透,让人醍醐灌顶,尤其闭包一节甚为精彩。难能可贵的是本书翻译也很棒,如果你对 Scala 不感冒,仅读 Java 部分也会受益颇多。

© 著作权归作者所有

共有 人打赏支持
joymufeng
粉丝 92
博文 70
码字总数 64279
作品 2
杨浦
高级程序员
私信 提问
加载中

评论(40)

d
dwingo

引用来自“dwingo”的评论

写法是简单了, 但上手还是加大了不少难度, 而且隐藏的坑只多不少, 另外更难以分析了解这些花式写法对性能的影响.

引用来自“joymufeng”的评论

Scala 的很多特性是在编译时完成的,不会影响运行时性能。Scala 2.13 的集合性能有了显著的提升,具体参考:https://www.scala-lang.org/blog/2018/02/09/collections-performance.html。另外Scala 2.13 之前的性能评测请参考:https://colobu.com/2016/11/17/Benchmarking-Scala-Collections/

引用来自“dwingo”的评论

Scala是可以写出性能很高的程序, 但这是在已经为Scala投入很多时间精力才能换来的, 需要对每个语法都有很透彻的理解, 如何编译成什么样的字节码, 需要多少内存分配, 有什么副作用, 最佳实践是什么, 这些东西都写清楚一千页的大部头都不够用, Java/JVM都要学很多东西才深入理解, Scala至少得用翻倍的时间精力. 而且复杂的语法带来的编译时间开销, 把Java对比C++这一大优势给抵消了. 最后运行时还得带一大坨jar.

引用来自“joymufeng”的评论

Scala 并不像你描述的那个样子,真正编码的时候没有人会关心字节码这种底层操作,那些应该是编译器和运行时该关心的事情。动手尝试一下可能会打消你很多的疑虑:smile:

引用来自“dwingo”的评论

不关心这些的结果就是你永远处于初级水平, 高手必须要弄通所用技术栈各层次原理, 遇到bug很快就能清楚哪里出问题, 遇到性能也能轻松找到瓶颈点. 如果只为了搬砖那就无视我说的吧, 不过想招些搬Scala砖的也不容易. Scala这么搞只会让用Go语言的极简敏捷主义者们更加鄙视,更被误解成Java环境很笨重,对新手不友好.

引用来自“joymufeng”的评论

你说的是通用的性能调优问题,无论是选择哪种语言,进阶时是不可避免的。

引用来自“dwingo”的评论

相比之下还是Java更容易进阶一些, 而且同样也很适用于大工程, 上手也相对简单, 又不像C/Go这样精简的语言虽然语法简单但做大工程就捉襟见肘了(抽象能力低). 我承认Kotlin,Scala确实能把代码写的少些, 但写代码的时间我觉得只占整个工程很小的一部分, 而且有IDE的辅助Java也不会多用多少时间, 过分精简代码容易导致阅读困难, 代码检查也因为代码冗余度低而难于发现代码中的bug. 语法特性过多也容易导致不同的人写法风格迥异, 做大工程难于调和(Python比Ruby流行的一大因素).

引用来自“joymufeng”的评论

目前 Java 的企业级开发地位是任何语言都无法撼动的,所以建议新手还是老老实实学 Java。但是作为经验丰富的老手或是公司技术领导者应该把眼光放远一点,毕竟都进入微服务时代了,不同的语言应该各领风骚。很多场景 Scala 更适合,比如说高并发,流处理,完全异步非阻塞系统的构建。

引用来自“dwingo”的评论

流处理不清楚, 但高并发和异步非阻塞Java也完全支持的很好. Java也有协程库.

引用来自“joymufeng”的评论

其实没必要陷入语言之争,这个话题无解。更不要把Java和Scala摆在对立的位置上,Scala完全拥抱现有的Java生态,并且提供了更多解决问题的工具,比如Macro,DSL,隐式转换,以及完整的函数式支持,这些都是Java不具备的,可以认为Scala是Java的一个有力补充,而不是竞争对手,大家其实是一家人,共同壮大JVM生态才是正道。

引用来自“dwingo”的评论

是你先发文挑起, 我只是摆事实, 说明一下文章回避的Java优势而已. 怎么选择让读者自己分辨. Java所选的特性都是优点明显大于缺点的. 而Macro等特性都是争议性很大的, 大多数语言不支持宏都是有道理的, C都支持的特性并不说明这个特性不好加, 而是说明怕这些特性被滥用宁愿不加而已. 提高开发效率的方式并不是加特性一种途径, 而利于阅读理解,利于查出bug,利于协作统一也是非常重要的环节. 优点不多的特性Java都很谨慎加入, 函数式也是挑重要的支持, Scala的一些写法都能换一种不太麻烦的Java写法实现.

引用来自“joymufeng”的评论

额~,此宏非彼宏,不可同日而语。其实全文的主题都在围绕Scala的"简洁性"展开,如果你阅读了全文,我想你应该能发现 Scala 并没有追求极致短码而导致不可读,而是在简洁和可读之间权衡的很好。所以本文的观点也非常明确,如果希望书写的代码更加简洁并且富有表达力,那么可以尝试下 Scala。Scala的很多设计都是为了改良Java,所以学习Scala的过程其实是对Java的一种深度回顾。真心希望大家可以浏览下《快学Scala》这本书,作者是Java语言大师,全文围绕Java与Scala展开,翻译也很棒,读完一定会大有收获。
简洁只是表面上的, 本质上还是多了一层封装, 遮蔽了复杂但时间久了迟早要面对, 现代C++也号称更加简洁, 但实际上也是表面上的, 用起来一点不轻松, 时刻要防范各种坑. 另外Java是最接近JVM的语言, 只要还在用JVM, 最终都是围绕着JVM规范. 如果是专门研究编程语言方向的, 那精通Scala没什么问题, 但绝大多数人是普通开发者, 不可能花大部分时间去研究语言本身, 开发涉及到的专业知识太多了.
joymufeng
joymufeng

引用来自“dwingo”的评论

写法是简单了, 但上手还是加大了不少难度, 而且隐藏的坑只多不少, 另外更难以分析了解这些花式写法对性能的影响.

引用来自“joymufeng”的评论

Scala 的很多特性是在编译时完成的,不会影响运行时性能。Scala 2.13 的集合性能有了显著的提升,具体参考:https://www.scala-lang.org/blog/2018/02/09/collections-performance.html。另外Scala 2.13 之前的性能评测请参考:https://colobu.com/2016/11/17/Benchmarking-Scala-Collections/

引用来自“dwingo”的评论

Scala是可以写出性能很高的程序, 但这是在已经为Scala投入很多时间精力才能换来的, 需要对每个语法都有很透彻的理解, 如何编译成什么样的字节码, 需要多少内存分配, 有什么副作用, 最佳实践是什么, 这些东西都写清楚一千页的大部头都不够用, Java/JVM都要学很多东西才深入理解, Scala至少得用翻倍的时间精力. 而且复杂的语法带来的编译时间开销, 把Java对比C++这一大优势给抵消了. 最后运行时还得带一大坨jar.

引用来自“joymufeng”的评论

Scala 并不像你描述的那个样子,真正编码的时候没有人会关心字节码这种底层操作,那些应该是编译器和运行时该关心的事情。动手尝试一下可能会打消你很多的疑虑:smile:

引用来自“dwingo”的评论

不关心这些的结果就是你永远处于初级水平, 高手必须要弄通所用技术栈各层次原理, 遇到bug很快就能清楚哪里出问题, 遇到性能也能轻松找到瓶颈点. 如果只为了搬砖那就无视我说的吧, 不过想招些搬Scala砖的也不容易. Scala这么搞只会让用Go语言的极简敏捷主义者们更加鄙视,更被误解成Java环境很笨重,对新手不友好.

引用来自“joymufeng”的评论

你说的是通用的性能调优问题,无论是选择哪种语言,进阶时是不可避免的。

引用来自“dwingo”的评论

相比之下还是Java更容易进阶一些, 而且同样也很适用于大工程, 上手也相对简单, 又不像C/Go这样精简的语言虽然语法简单但做大工程就捉襟见肘了(抽象能力低). 我承认Kotlin,Scala确实能把代码写的少些, 但写代码的时间我觉得只占整个工程很小的一部分, 而且有IDE的辅助Java也不会多用多少时间, 过分精简代码容易导致阅读困难, 代码检查也因为代码冗余度低而难于发现代码中的bug. 语法特性过多也容易导致不同的人写法风格迥异, 做大工程难于调和(Python比Ruby流行的一大因素).

引用来自“joymufeng”的评论

目前 Java 的企业级开发地位是任何语言都无法撼动的,所以建议新手还是老老实实学 Java。但是作为经验丰富的老手或是公司技术领导者应该把眼光放远一点,毕竟都进入微服务时代了,不同的语言应该各领风骚。很多场景 Scala 更适合,比如说高并发,流处理,完全异步非阻塞系统的构建。

引用来自“dwingo”的评论

流处理不清楚, 但高并发和异步非阻塞Java也完全支持的很好. Java也有协程库.

引用来自“joymufeng”的评论

其实没必要陷入语言之争,这个话题无解。更不要把Java和Scala摆在对立的位置上,Scala完全拥抱现有的Java生态,并且提供了更多解决问题的工具,比如Macro,DSL,隐式转换,以及完整的函数式支持,这些都是Java不具备的,可以认为Scala是Java的一个有力补充,而不是竞争对手,大家其实是一家人,共同壮大JVM生态才是正道。

引用来自“dwingo”的评论

是你先发文挑起, 我只是摆事实, 说明一下文章回避的Java优势而已. 怎么选择让读者自己分辨. Java所选的特性都是优点明显大于缺点的. 而Macro等特性都是争议性很大的, 大多数语言不支持宏都是有道理的, C都支持的特性并不说明这个特性不好加, 而是说明怕这些特性被滥用宁愿不加而已. 提高开发效率的方式并不是加特性一种途径, 而利于阅读理解,利于查出bug,利于协作统一也是非常重要的环节. 优点不多的特性Java都很谨慎加入, 函数式也是挑重要的支持, Scala的一些写法都能换一种不太麻烦的Java写法实现.
额~,此宏非彼宏,不可同日而语。其实全文的主题都在围绕Scala的"简洁性"展开,如果你阅读了全文,我想你应该能发现 Scala 并没有追求极致短码而导致不可读,而是在简洁和可读之间权衡的很好。所以本文的观点也非常明确,如果希望书写的代码更加简洁并且富有表达力,那么可以尝试下 Scala。Scala的很多设计都是为了改良Java,所以学习Scala的过程其实是对Java的一种深度回顾。真心希望大家可以浏览下《快学Scala》这本书,作者是Java语言大师,全文围绕Java与Scala展开,翻译也很棒,读完一定会大有收获。
d
dwingo

引用来自“dwingo”的评论

写法是简单了, 但上手还是加大了不少难度, 而且隐藏的坑只多不少, 另外更难以分析了解这些花式写法对性能的影响.

引用来自“joymufeng”的评论

Scala 的很多特性是在编译时完成的,不会影响运行时性能。Scala 2.13 的集合性能有了显著的提升,具体参考:https://www.scala-lang.org/blog/2018/02/09/collections-performance.html。另外Scala 2.13 之前的性能评测请参考:https://colobu.com/2016/11/17/Benchmarking-Scala-Collections/

引用来自“dwingo”的评论

Scala是可以写出性能很高的程序, 但这是在已经为Scala投入很多时间精力才能换来的, 需要对每个语法都有很透彻的理解, 如何编译成什么样的字节码, 需要多少内存分配, 有什么副作用, 最佳实践是什么, 这些东西都写清楚一千页的大部头都不够用, Java/JVM都要学很多东西才深入理解, Scala至少得用翻倍的时间精力. 而且复杂的语法带来的编译时间开销, 把Java对比C++这一大优势给抵消了. 最后运行时还得带一大坨jar.

引用来自“joymufeng”的评论

Scala 并不像你描述的那个样子,真正编码的时候没有人会关心字节码这种底层操作,那些应该是编译器和运行时该关心的事情。动手尝试一下可能会打消你很多的疑虑:smile:

引用来自“dwingo”的评论

不关心这些的结果就是你永远处于初级水平, 高手必须要弄通所用技术栈各层次原理, 遇到bug很快就能清楚哪里出问题, 遇到性能也能轻松找到瓶颈点. 如果只为了搬砖那就无视我说的吧, 不过想招些搬Scala砖的也不容易. Scala这么搞只会让用Go语言的极简敏捷主义者们更加鄙视,更被误解成Java环境很笨重,对新手不友好.

引用来自“joymufeng”的评论

你说的是通用的性能调优问题,无论是选择哪种语言,进阶时是不可避免的。

引用来自“dwingo”的评论

相比之下还是Java更容易进阶一些, 而且同样也很适用于大工程, 上手也相对简单, 又不像C/Go这样精简的语言虽然语法简单但做大工程就捉襟见肘了(抽象能力低). 我承认Kotlin,Scala确实能把代码写的少些, 但写代码的时间我觉得只占整个工程很小的一部分, 而且有IDE的辅助Java也不会多用多少时间, 过分精简代码容易导致阅读困难, 代码检查也因为代码冗余度低而难于发现代码中的bug. 语法特性过多也容易导致不同的人写法风格迥异, 做大工程难于调和(Python比Ruby流行的一大因素).

引用来自“joymufeng”的评论

目前 Java 的企业级开发地位是任何语言都无法撼动的,所以建议新手还是老老实实学 Java。但是作为经验丰富的老手或是公司技术领导者应该把眼光放远一点,毕竟都进入微服务时代了,不同的语言应该各领风骚。很多场景 Scala 更适合,比如说高并发,流处理,完全异步非阻塞系统的构建。

引用来自“dwingo”的评论

流处理不清楚, 但高并发和异步非阻塞Java也完全支持的很好. Java也有协程库.

引用来自“joymufeng”的评论

其实没必要陷入语言之争,这个话题无解。更不要把Java和Scala摆在对立的位置上,Scala完全拥抱现有的Java生态,并且提供了更多解决问题的工具,比如Macro,DSL,隐式转换,以及完整的函数式支持,这些都是Java不具备的,可以认为Scala是Java的一个有力补充,而不是竞争对手,大家其实是一家人,共同壮大JVM生态才是正道。
是你先发文挑起, 我只是摆事实, 说明一下文章回避的Java优势而已. 怎么选择让读者自己分辨. Java所选的特性都是优点明显大于缺点的. 而Macro等特性都是争议性很大的, 大多数语言不支持宏都是有道理的, C都支持的特性并不说明这个特性不好加, 而是说明怕这些特性被滥用宁愿不加而已. 提高开发效率的方式并不是加特性一种途径, 而利于阅读理解,利于查出bug,利于协作统一也是非常重要的环节. 优点不多的特性Java都很谨慎加入, 函数式也是挑重要的支持, Scala的一些写法都能换一种不太麻烦的Java写法实现.
joymufeng
joymufeng

引用来自“海淀游民”的评论

智商不够,用不了 Scala (逃)

引用来自“漂浪天下”的评论

同感

引用来自“joymufeng”的评论

可以先玩玩试试,也许你会有新发现

引用来自“烟波”的评论

感觉有些特性和Common Lisp有点像,类似函数式编程那套理论,最近接触的几个新语言好像都有这个趋势
嗯,Scala 生态比较成熟。
joymufeng
joymufeng

引用来自“dwingo”的评论

写法是简单了, 但上手还是加大了不少难度, 而且隐藏的坑只多不少, 另外更难以分析了解这些花式写法对性能的影响.

引用来自“joymufeng”的评论

Scala 的很多特性是在编译时完成的,不会影响运行时性能。Scala 2.13 的集合性能有了显著的提升,具体参考:https://www.scala-lang.org/blog/2018/02/09/collections-performance.html。另外Scala 2.13 之前的性能评测请参考:https://colobu.com/2016/11/17/Benchmarking-Scala-Collections/

引用来自“dwingo”的评论

Scala是可以写出性能很高的程序, 但这是在已经为Scala投入很多时间精力才能换来的, 需要对每个语法都有很透彻的理解, 如何编译成什么样的字节码, 需要多少内存分配, 有什么副作用, 最佳实践是什么, 这些东西都写清楚一千页的大部头都不够用, Java/JVM都要学很多东西才深入理解, Scala至少得用翻倍的时间精力. 而且复杂的语法带来的编译时间开销, 把Java对比C++这一大优势给抵消了. 最后运行时还得带一大坨jar.

引用来自“joymufeng”的评论

Scala 并不像你描述的那个样子,真正编码的时候没有人会关心字节码这种底层操作,那些应该是编译器和运行时该关心的事情。动手尝试一下可能会打消你很多的疑虑:smile:

引用来自“dwingo”的评论

不关心这些的结果就是你永远处于初级水平, 高手必须要弄通所用技术栈各层次原理, 遇到bug很快就能清楚哪里出问题, 遇到性能也能轻松找到瓶颈点. 如果只为了搬砖那就无视我说的吧, 不过想招些搬Scala砖的也不容易. Scala这么搞只会让用Go语言的极简敏捷主义者们更加鄙视,更被误解成Java环境很笨重,对新手不友好.

引用来自“joymufeng”的评论

你说的是通用的性能调优问题,无论是选择哪种语言,进阶时是不可避免的。

引用来自“dwingo”的评论

相比之下还是Java更容易进阶一些, 而且同样也很适用于大工程, 上手也相对简单, 又不像C/Go这样精简的语言虽然语法简单但做大工程就捉襟见肘了(抽象能力低). 我承认Kotlin,Scala确实能把代码写的少些, 但写代码的时间我觉得只占整个工程很小的一部分, 而且有IDE的辅助Java也不会多用多少时间, 过分精简代码容易导致阅读困难, 代码检查也因为代码冗余度低而难于发现代码中的bug. 语法特性过多也容易导致不同的人写法风格迥异, 做大工程难于调和(Python比Ruby流行的一大因素).

引用来自“joymufeng”的评论

目前 Java 的企业级开发地位是任何语言都无法撼动的,所以建议新手还是老老实实学 Java。但是作为经验丰富的老手或是公司技术领导者应该把眼光放远一点,毕竟都进入微服务时代了,不同的语言应该各领风骚。很多场景 Scala 更适合,比如说高并发,流处理,完全异步非阻塞系统的构建。

引用来自“dwingo”的评论

流处理不清楚, 但高并发和异步非阻塞Java也完全支持的很好. Java也有协程库.
其实没必要陷入语言之争,这个话题无解。更不要把Java和Scala摆在对立的位置上,Scala完全拥抱现有的Java生态,并且提供了更多解决问题的工具,比如Macro,DSL,隐式转换,以及完整的函数式支持,这些都是Java不具备的,可以认为Scala是Java的一个有力补充,而不是竞争对手,大家其实是一家人,共同壮大JVM生态才是正道。
烟波
烟波

引用来自“海淀游民”的评论

智商不够,用不了 Scala (逃)

引用来自“漂浪天下”的评论

同感

引用来自“joymufeng”的评论

可以先玩玩试试,也许你会有新发现
感觉有些特性和Common Lisp有点像,类似函数式编程那套理论,最近接触的几个新语言好像都有这个趋势
d
dwingo

引用来自“dwingo”的评论

写法是简单了, 但上手还是加大了不少难度, 而且隐藏的坑只多不少, 另外更难以分析了解这些花式写法对性能的影响.

引用来自“joymufeng”的评论

Scala 的很多特性是在编译时完成的,不会影响运行时性能。Scala 2.13 的集合性能有了显著的提升,具体参考:https://www.scala-lang.org/blog/2018/02/09/collections-performance.html。另外Scala 2.13 之前的性能评测请参考:https://colobu.com/2016/11/17/Benchmarking-Scala-Collections/

引用来自“dwingo”的评论

Scala是可以写出性能很高的程序, 但这是在已经为Scala投入很多时间精力才能换来的, 需要对每个语法都有很透彻的理解, 如何编译成什么样的字节码, 需要多少内存分配, 有什么副作用, 最佳实践是什么, 这些东西都写清楚一千页的大部头都不够用, Java/JVM都要学很多东西才深入理解, Scala至少得用翻倍的时间精力. 而且复杂的语法带来的编译时间开销, 把Java对比C++这一大优势给抵消了. 最后运行时还得带一大坨jar.

引用来自“joymufeng”的评论

Scala 并不像你描述的那个样子,真正编码的时候没有人会关心字节码这种底层操作,那些应该是编译器和运行时该关心的事情。动手尝试一下可能会打消你很多的疑虑:smile:

引用来自“dwingo”的评论

不关心这些的结果就是你永远处于初级水平, 高手必须要弄通所用技术栈各层次原理, 遇到bug很快就能清楚哪里出问题, 遇到性能也能轻松找到瓶颈点. 如果只为了搬砖那就无视我说的吧, 不过想招些搬Scala砖的也不容易. Scala这么搞只会让用Go语言的极简敏捷主义者们更加鄙视,更被误解成Java环境很笨重,对新手不友好.

引用来自“joymufeng”的评论

你说的是通用的性能调优问题,无论是选择哪种语言,进阶时是不可避免的。

引用来自“dwingo”的评论

相比之下还是Java更容易进阶一些, 而且同样也很适用于大工程, 上手也相对简单, 又不像C/Go这样精简的语言虽然语法简单但做大工程就捉襟见肘了(抽象能力低). 我承认Kotlin,Scala确实能把代码写的少些, 但写代码的时间我觉得只占整个工程很小的一部分, 而且有IDE的辅助Java也不会多用多少时间, 过分精简代码容易导致阅读困难, 代码检查也因为代码冗余度低而难于发现代码中的bug. 语法特性过多也容易导致不同的人写法风格迥异, 做大工程难于调和(Python比Ruby流行的一大因素).

引用来自“joymufeng”的评论

目前 Java 的企业级开发地位是任何语言都无法撼动的,所以建议新手还是老老实实学 Java。但是作为经验丰富的老手或是公司技术领导者应该把眼光放远一点,毕竟都进入微服务时代了,不同的语言应该各领风骚。很多场景 Scala 更适合,比如说高并发,流处理,完全异步非阻塞系统的构建。
流处理不清楚, 但高并发和异步非阻塞Java也完全支持的很好. Java也有协程库.
joymufeng
joymufeng

引用来自“dwingo”的评论

写法是简单了, 但上手还是加大了不少难度, 而且隐藏的坑只多不少, 另外更难以分析了解这些花式写法对性能的影响.

引用来自“joymufeng”的评论

Scala 的很多特性是在编译时完成的,不会影响运行时性能。Scala 2.13 的集合性能有了显著的提升,具体参考:https://www.scala-lang.org/blog/2018/02/09/collections-performance.html。另外Scala 2.13 之前的性能评测请参考:https://colobu.com/2016/11/17/Benchmarking-Scala-Collections/

引用来自“dwingo”的评论

Scala是可以写出性能很高的程序, 但这是在已经为Scala投入很多时间精力才能换来的, 需要对每个语法都有很透彻的理解, 如何编译成什么样的字节码, 需要多少内存分配, 有什么副作用, 最佳实践是什么, 这些东西都写清楚一千页的大部头都不够用, Java/JVM都要学很多东西才深入理解, Scala至少得用翻倍的时间精力. 而且复杂的语法带来的编译时间开销, 把Java对比C++这一大优势给抵消了. 最后运行时还得带一大坨jar.

引用来自“joymufeng”的评论

Scala 并不像你描述的那个样子,真正编码的时候没有人会关心字节码这种底层操作,那些应该是编译器和运行时该关心的事情。动手尝试一下可能会打消你很多的疑虑:smile:

引用来自“dwingo”的评论

不关心这些的结果就是你永远处于初级水平, 高手必须要弄通所用技术栈各层次原理, 遇到bug很快就能清楚哪里出问题, 遇到性能也能轻松找到瓶颈点. 如果只为了搬砖那就无视我说的吧, 不过想招些搬Scala砖的也不容易. Scala这么搞只会让用Go语言的极简敏捷主义者们更加鄙视,更被误解成Java环境很笨重,对新手不友好.

引用来自“joymufeng”的评论

你说的是通用的性能调优问题,无论是选择哪种语言,进阶时是不可避免的。

引用来自“dwingo”的评论

相比之下还是Java更容易进阶一些, 而且同样也很适用于大工程, 上手也相对简单, 又不像C/Go这样精简的语言虽然语法简单但做大工程就捉襟见肘了(抽象能力低). 我承认Kotlin,Scala确实能把代码写的少些, 但写代码的时间我觉得只占整个工程很小的一部分, 而且有IDE的辅助Java也不会多用多少时间, 过分精简代码容易导致阅读困难, 代码检查也因为代码冗余度低而难于发现代码中的bug. 语法特性过多也容易导致不同的人写法风格迥异, 做大工程难于调和(Python比Ruby流行的一大因素).
目前 Java 的企业级开发地位是任何语言都无法撼动的,所以建议新手还是老老实实学 Java。但是作为经验丰富的老手或是公司技术领导者应该把眼光放远一点,毕竟都进入微服务时代了,不同的语言应该各领风骚。很多场景 Scala 更适合,比如说高并发,流处理,完全异步非阻塞系统的构建。
gowk
gowk
基本同意dwingo的观点,我觉得Scala, Rust这些对新手不友好的语言在中国根本没有立足之地,老老实实用Java,Go比什么都强
d
dwingo

引用来自“dwingo”的评论

写法是简单了, 但上手还是加大了不少难度, 而且隐藏的坑只多不少, 另外更难以分析了解这些花式写法对性能的影响.

引用来自“joymufeng”的评论

Scala 的很多特性是在编译时完成的,不会影响运行时性能。Scala 2.13 的集合性能有了显著的提升,具体参考:https://www.scala-lang.org/blog/2018/02/09/collections-performance.html。另外Scala 2.13 之前的性能评测请参考:https://colobu.com/2016/11/17/Benchmarking-Scala-Collections/

引用来自“dwingo”的评论

Scala是可以写出性能很高的程序, 但这是在已经为Scala投入很多时间精力才能换来的, 需要对每个语法都有很透彻的理解, 如何编译成什么样的字节码, 需要多少内存分配, 有什么副作用, 最佳实践是什么, 这些东西都写清楚一千页的大部头都不够用, Java/JVM都要学很多东西才深入理解, Scala至少得用翻倍的时间精力. 而且复杂的语法带来的编译时间开销, 把Java对比C++这一大优势给抵消了. 最后运行时还得带一大坨jar.

引用来自“joymufeng”的评论

Scala 并不像你描述的那个样子,真正编码的时候没有人会关心字节码这种底层操作,那些应该是编译器和运行时该关心的事情。动手尝试一下可能会打消你很多的疑虑:smile:

引用来自“dwingo”的评论

不关心这些的结果就是你永远处于初级水平, 高手必须要弄通所用技术栈各层次原理, 遇到bug很快就能清楚哪里出问题, 遇到性能也能轻松找到瓶颈点. 如果只为了搬砖那就无视我说的吧, 不过想招些搬Scala砖的也不容易. Scala这么搞只会让用Go语言的极简敏捷主义者们更加鄙视,更被误解成Java环境很笨重,对新手不友好.

引用来自“joymufeng”的评论

你说的是通用的性能调优问题,无论是选择哪种语言,进阶时是不可避免的。
相比之下还是Java更容易进阶一些, 而且同样也很适用于大工程, 上手也相对简单, 又不像C/Go这样精简的语言虽然语法简单但做大工程就捉襟见肘了(抽象能力低). 我承认Kotlin,Scala确实能把代码写的少些, 但写代码的时间我觉得只占整个工程很小的一部分, 而且有IDE的辅助Java也不会多用多少时间, 过分精简代码容易导致阅读困难, 代码检查也因为代码冗余度低而难于发现代码中的bug. 语法特性过多也容易导致不同的人写法风格迥异, 做大工程难于调和(Python比Ruby流行的一大因素).
挑逗Java 程序员的那些 Scala 绝技

有个问题一直困扰着 Scala 社区,为什么一些 Java 开发者将 Scala 捧到了天上,认为它是来自上帝之吻的完美语言;而另外一些 Java 开发者却对它望而却步,认为它过于复杂而难以理解。同样是 ...

沧海一刀
10/24
0
0
挑逗Java程序员的那些Scala绝技

有个问题一直困扰着 Scala 社区,为什么一些 Java 开发者将 Scala 捧到了天上,认为它是来自上帝之吻的完美语言;而另外一些 Java 开发者却对它望而却步,认为它过于复杂而难以理解。同样是 ...

大数据之路
2012/09/28
0
2
专访《Java程序员修炼之道》作者Ben Evans

转帖这篇文章,很大成分就是认同“保守的设计思想是Java的最大优势”这句话 ------------------------------------------------------------------ 【编者按】Java是一种可以撰写跨平台应用软...

深蓝苹果
2014/10/23
0
0
资深架构师谈Java——最牛逼的编程语言

有些人问我,在现有的语言里面,有什么好的推荐?我说:“Java。” 他们很惊讶:“什么?Java!” 所以我现在来解释一下。 Java超越了所有咒骂它的“动态语言” 也许是因为年轻人的逆反心理,...

美的让人心动
04/18
0
0
对象函数式编程 Scala简史

本文概述了Scala语言诞生过程中的各种软件开发运动历史的事件,当人们研究Scala时,发现这是一种给人印象深刻的语言,但是由于这种语言的功能特征不断的急速进化,导致除了一些自己研究的项目...

mj4738
2011/11/01
0
1

没有更多内容

加载失败,请刷新页面

加载更多

Mariadb二进制包安装,Apache安装

安装mariadb 下载二进制包并解压 [root@test-a src]# wget https://downloads.mariadb.com/MariaDB/mariadb-10.2.6/bintar-linux-glibc_214-x86_64/mariadb-10.2.6-linux-glibc_214-x86_64.t......

野雪球
6分钟前
0
0
ConcurrentHashMap 高并发性的实现机制

ConcurrentHashMap 的结构分析 为了更好的理解 ConcurrentHashMap 高并发的具体实现,让我们先探索它的结构模型。 ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEnt...

TonyStarkSir
今天
3
0
大数据教程(7.4)HDFS的java客户端API(流处理方式)

博主上一篇博客分享了namenode和datanode的工作原理,本章节将继前面的HDFS的java客户端简单API后深度讲述HDFS流处理API。 场景:博主前面的文章介绍过HDFS上存的大文件会成不同的块存储在不...

em_aaron
昨天
2
0
聊聊storm的window trigger

序 本文主要研究一下storm的window trigger WindowTridentProcessor.prepare storm-core-1.2.2-sources.jar!/org/apache/storm/trident/windowing/WindowTridentProcessor.java public v......

go4it
昨天
6
0
CentOS 生产环境配置

初始配置 对于一般配置来说,不需要安装 epel-release 仓库,本文主要在于希望跟随 RHEL 的配置流程,紧跟红帽公司对于服务器的配置说明。 # yum update 安装 centos-release-scl # yum ins...

clin003
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部