Java/Scala杂记之一


Overview

最近工作中都是些Java/Scala的项目,这块的技术栈(Technology stack,咱也赶个时髦)都停留在5年前了,现在需要补充一下,平时遇到的东西就随手摘抄一下好了。

Scala

Option

Scala里的Option非常好用,主要就是取代了Java里的null,避免了漫天飞的NullPointerException。结合case match简直是神器啊,例如

val test: Option[String] = //someting
test match {
  Some(v) => doSometingWithString(v)
  None => doSometingWithNull()
}

scala.util.Try

Try是一个让人兴奋的“语法糖”,用于取代Java非常讨厌的try-catch,结合模式匹配,比繁琐的try-catch方便多了,话不多说,直接上例子:

def updateKey(): Unit = {
  rowkey = Try(MessageDigest.getInstance("MD5")) match {
    case Success(digger) => Express.byte2hex(digger.digest(filecont.getBytes))
    case _ => f"${MurmurHash3.stringHash(filecont)}%08x"
  }   
}

这里是一个如果调用Java的md5库失败,就用Scala的MurmurHash3的用法,使用了Try和case match直接让代码变得非常简洁。

字符串插值

字符串插值一直是我觉得perl用起来非常爽的东西,现在Scala也支持了,而且还有一种带格式的,简直爽到不要不要的。

val lll = List("s1", "s2")
val a = 0xabcd
val str1 = s"I am ${lll(0)}"
val str2 = s"number is $a"
val str3 = f"number in hex is $a%x"

fold/foldLeft/foldRight

其实fp范式对于集合操作foreach/map/filter/fold/flatMap等等都非常好用,这也是为什么个别函数式语言连循环都没有的原因,写起来逼格特别高,这里给一个byte数组生成十六进制字符串表示的例子,Java版和Scala版都上:

def byte2hex(arr: Array[Byte]): String = {
  arr.map(n => f"${n & 0xff}%02x").foldLeft(new StringBuilder())((sb, str) => sb.append(str)).toString()
}
public static String byte2hex(byte[] arr) {
    StringBuilder sb = new StringBuilder();
    for (byte b: arr) {
        sb.append(Integer.toHexString(b&0xff));
    }   
    return sb.toString();
}

虽然说Java版的看上去可读性更好一点,更符合思维习惯,但是Scala版本J的简直帅爆了有没有,各种one-line code。

工具类

jsoup

第一个工具是jsoup,虽然说对于Scala,xml是一等公民,但是坑爹是html它不是标准的xml,只能说近似xml,所以直接用Scala的xml库会遇到各种问题,这里只能用第三方库了。jsoup是Java写的库,用起来还是挺方便的,功能也很强大,所以这里就果断学习了一下。

我用jsoup的场景是从网页里抽取我要的信息,基本就是一个xpath的用法。基本上jsoup提供了两种api,一种是js的dom的方式,另外一种是xpath的selector式的,我主要是用第二种,下面上个我从什么值得买的发现频道目录页抽取正文页url的代码片段,一般来说网页抽取是以xpath+正则过滤的方式,所以这里也是这样做的。

private val hrefRegex = new Regex("""/p/\d+/$""")
def parseUrl(result: Result) = {
  val doc = Jsoup.parse(Bytes.toString(result.getValue(Bytes.toBytes("dt"), Bytes.toBytes("cont"))), "http://faxian.smzdm.com/")
  //select在scala这种fp范式语言里用着简直爽
  doc.select("a").map(n => n.attr("href")).filter(urlFilter)
}
def urlFilter(url: String) = {
  hrefRegex.findFirstIn(url) match {
    case Some(hrefRegex()) => true
    case _ => false
  }
}

Scala/Java混合项目的pom文件写法

公司里大多数人是用Java的,但是我不是特别喜欢写Java,毕竟好马不吃回头草,最近一个项目和同事合作的一个项目,必须用Java,但很多业务逻辑(比如SQL查询生成)用Scala写非常方便,Java和Scala之间互相调用比较方便,所以就准备用Scala写一部分业务逻辑,对外接口全是Java的。Java项目用Maven比较多,而Scala都是用sbt,在写pom.xml时,混合项目踩了不少坑,不太清楚sbt有没有这个问题。

Scala官网给的范例都太老了,根本没法用,google了半天才在github上找到了个样板配置,把<build></build>之间的插件配上去就可以了。这里不贴配置了,大家去那个github项目clone吧。

Jetty

Jetty是eclipse的一个项目,是一个web server,这里主要用Jetty的http client,基于netty/nio之类的做的,性能应该比较靠谱,所以需要用到http client的地方就准备用Jetty了。

虽然说Scala/Java本身也提供一些http api,但是毕竟缺少封装,用起来还是麻烦。

val jettyClient = new HttpClient
jettyClient.setFollowRedirects(true)
jettyClient.start()
val res = jettyClient.newRequest("http://somehost/phoenix/dml").param("query", "SELECT DATA_VALUE,DATA_STATUS FROM DATA_STATUS WHERE DATA_KEY='test';").send().getContentAsString()
jettyClient.stop()

pom配置:

<dependency>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-client</artifactId>
  <version>9.3.6.v20151106</version>
</dependency>

play-json

Scala的JSON库倒是也挺多的,各种web框架几乎都有自己的库,什么play-json, lift-json的。说起play,这是当年看走眼的一个框架,最早play1.0的年代是一个Java框架,印象中和servlet/jsp那套很不一样,当时觉得理念接收着别扭,所以没怎么研究过,还是搞起了熟悉的servlet/jsp,没想到这货现在发展的这么好,typesafe的一员啊。

当初玩Scala选择的web框架是lift,如今貌似势头没有play2好,不过之前在stackoverflow上看到一篇比较贴,lift的作者David Pollak还亲自回了,对play2不屑一顾,感兴趣的同学可以看看

话说回来,play-json用着还比较简单,语法上有点像scala的xml库的\,所以用着比较熟悉,直接上一段代码吧。

pom配置,比较蛋疼的是play-json依赖的jackson和其他包冲突,所以这里展示一下exclusion的用法。

<dependency>
  <groupId>com.typesafe.play</groupId>
  <artifactId>play-json_2.10</artifactId>
  <version>2.4.4</version>
  <exclusions>
    <exclusion>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
    </exclusion>
    <exclusion>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.5.0</version>
</dependency>
//返回是{"result":[["t1","1"]]}类似这样
val json = (Json.parse(ret) \ "result")(0)(1).asOpt[String] match {
  case Some(v) => v.toInt
  case None => -1
}

下集预告

最近事情比较多,所以这篇只能简单记录下用到的一些东西吧。最近的项目是基于spray做的一个restful服务,下一篇可能会说spray, akka, slick什么的吧。

Reference


文章作者: Odin
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Odin !
  目录