半岛外围网上直营

Java开发中Scala的使用指南(二)

原创|其它|编辑:郝浩|2009-09-03 11:13:56.000|阅读 1466 次

概述:抽象地谈论 Scala 的确有趣,然而一旦将其付诸实践,就会发现将它作为 “玩具” 与在工作中使用它的区别。Scala 狂热者 Ted Neward 撰写了一篇 对 Scitter 的介绍,Scitter 是一个用于访问 Twitter 的 Scala 库,本文是其后续篇,在本文中,Ted Neward 为这个客户机库提供了一组更有趣也更有用的特性。

#慧都22周年庆大促·界面/图表报表/文档/IDE/IOT/测试等千款热门软控件火热促销中>>

  连接(到朋友)

  Twitter API 表明,friends_timeline API 调用 “返回认证用户和该用户的朋友发表的最近 20 条状态”。(它还指出,对于直接从 Twitter Web 站点使用 Twitter 的用户,“这相当于 Web 上的 ‘/home’”。)对于任何 Twitter API 来说,这是非常基本的要求,所以让我们将它添加到 Scitter 类中。之所以将它添加到类而不是对象中,是因为正如文档中指出的那样,这是以验证用户的身份做的事情,而我已经决定归入 Scitter 类,而不是 Scitter 对象。

  但是,这里我们碰到一块绊脚石:friends_timeline 调用接受一组 “可选参数”,包括 since_id、max_id、count 和 page,以控制返回的结果。这是一项比较复杂的操作,因为 Scala 不像其他语言(例如 Groovy、JRuby 或 JavaScript)那样原生地支持 “可选参数” 的思想,但是我们首先来处理简单的东西 — 我们来创建一个 friendsTimeline 方法,该方法只执行一般的、非参数化的调用:

  清单 6.“告诉我你身边的朋友是怎样的……”

 package com.tedneward.scitter
  {
  class Scitter
  {
  def friendsTimeline : List[Status] =
  {
  val (statusCode, statusBody) =
  Scitter.execute("//twitter.com/statuses/friends_timeline.xml",
  username, password)
  if (statusCode == 200)
  {
  val responseXML = XML.loadString(statusBody)
  val statusListBuffer = new ListBuffer[Status]
  for (n <- (responseXML \\ "status").elements)
  statusListBuffer += (Status.fromXml(n))
  statusListBuffer.toList
  }
  else
  {
  Nil
  }
  }
  }
  }

  到目前为止,一切良好。用于测试的相应方法看上去如下所示:

  清单 7. “我能判断您是怎样的人 ”(Miguel de Cervantes)

 package com.tedneward.scitter.test
  {
  class ScitterTests
  {
  // ...
  @Test def scitterFriendsTimeline =
  {
  val scitter = new Scitter(testUser, testPassword)
  val result = scitter.friendsTimeline
  assertTrue(result.length > 0)
  }
  }
  }

  好极了。看上去就像 Scitter 对象中的 publicTimeline() 方法,并且行为也几乎完全相同。

  对于我们来说,那些可选参数仍然有问题。因为 Scala 并没有可选参数这样的语言特性,乍一看来,惟一的选择就是完整地创建重载的 friendsTimeline() 方法,让该方法带有一定数量的参数。

  幸运的是,还有一种更好的方式,即通过一种有趣的方式将 Scala 的两个语言特性(有一个特性我还没有提到过) — case 类和 “重复参数” 结合起来(见清单 8):

  清单 8. “我有多爱你?……”

  package com.tedneward.scitter
  {
  // ...
  abstract class OptionalParam
  case class Id(id : String) extends OptionalParam
  case class UserId(id : Long) extends OptionalParam
  case class Since(since_id : Long) extends OptionalParam
  case class Max(max_id : Long) extends OptionalParam
  case class Count(count : Int) extends OptionalParam
  case class Page(page : Int) extends OptionalParam
  class Scitter(username : String, password : String)
  {
  // ...
  def friendsTimeline(options : OptionalParam*) : List[Status] =
  {
  val optionsStr =
  new StringBuffer("//twitter.com/statuses/friends_timeline.xml?")
  for (option <- options)
  {
  option match
  {
  case Since(since_id) =>
  optionsStr.append("since_id=" + since_id.toString() + "&")
  case Max(max_id) =>
  optionsStr.append("max_id=" + max_id.toString() + "&")
  case Count(count) =>
  optionsStr.append("count=" + count.toString() + "&")
  case Page(page) =>
  optionsStr.append("page=" + page.toString() + "&")
  }
  }
  val (statusCode, statusBody) =
  Scitter.execute(optionsStr.toString(), username, password)
  if (statusCode == 200)
  {
  val responseXML = XML.loadString(statusBody)
  val statusListBuffer = new ListBuffer[Status]
  for (n <- (responseXML \\ "status").elements)
  statusListBuffer += (Status.fromXml(n))
  statusListBuffer.toList
  }
  else
  {
  Nil
  }
  }
  }
  }

  看到标在选项参数后面的 * 吗?这表明该参数实际上是一个参数序列,这类似于 Java 5 中的 varargs 结构。和 varargs 一样,传递的参数数量可以像前面那样为 0(不过,我们将需要回到测试代码,向 friendsTimeline 调用增加一对括号,否则编译器无法作出判断:是调用不带参数的方法,还是出于部分应用之类的目的而调用该方法);我们还可以开始传递那些 case 类型,如下面的清单所示:

  清单 9. “……听我细细说”(William Shakespeare)

package com.tedneward.scitter.test
  {
  class ScitterTests
  {
  // ...
  @Test def scitterFriendsTimelineWithCount =
  {
  val scitter = new Scitter(testUser, testPassword)
  val result = scitter.friendsTimeline(Count(5))
  assertTrue(result.length == 5)
  }
  }
  }

  当然,总是存在这样的可能性:客户机传入古怪的参数序列,例如 friendsTimeline(Count(5), Count(6), Count(7)),但是在这里,我们只是将列表传递给 Twitter(希望它们的错误处理足够强大,能够只采用指定的最后那个参数)。当然,如果真的担心这一点,也很容易在构造发送到 Twitter 的 URL 之前,从头至尾检查重复参数列表,并采用指定的每种参数的最后一个参数。不过,后果自负。

  兼容性

  但是,这又产生一个有趣的问题:从 Java 代码调用这个方法有多容易?毕竟,如果这个库的主要目标之一是维护与 Java 代码的兼容性,那么我们需要确保 Java 代码在使用它时不至于太麻烦。

  我们首先通过我们的好朋友 javap 检验一下 Scitter 类:

  清单 10. 哦,没错,Java 代码……我现在想起来了……

 C:\>javap -classpath classes com.tedneward.scitter.Scitter
  Compiled from "scitter.scala"
  public class com.tedneward.scitter.Scitter extends java.lang.Object implements s
  cala.ScalaObject{
  public com.tedneward.scitter.Scitter(java.lang.String, java.lang.String);
  public scala.List friendsTimeline(scala.Seq);
  public boolean verifyCredentials();
  public int $tag() throws java.rmi.RemoteException;
  }

  这时我心中有两点担心。首先,friendsTimeline() 带有一个 scala.Seq 参数(这是我们刚才用过的重复参数特性)。其次,friendsTimeline() 方法和 Scitter 对象中的 publicTimeline() 方法一样(如果不信,可以运行 javap 查证),返回一个元素列表 scala.List.这两种类型在 Java 代码中有多好用?

  最简单的方法是用 Java 代码而不是 Scala 编写一组小型的 JUnit 测试,所以接下来我们就这样做。虽然可以测试 Scitter 实例的构造,并调用它的 verifyCredentials() 方法,但这些并不是特别有用 — 记住,我们不是要验证 Scitter 类的正确性,而是要看看从 Java 代码中使用它有多容易。为此,我们直接编写一个测试,该测试将获取 “friends timeline” — 换句话说,我们要实例化一个 Scitter 实例,并且不使用任何参数来调用它的 friendsTimeline() 方法。

  这有点复杂,因为需要传入 scala.Seq 参数 — scala.Seq 是一个 Scala 特性,它将映射到底层 JVM 中的一个接口,所以不能直接实例化。我们可以尝试典型的 Java null 参数,但是这样做会在运行时抛出异常。我们需要的是一个 scala.Seq 类,以便从 Java 代码中轻松地实例化这个类。

  最终,我们还是在 mutable.ListBuffer 类型中找到一个这样的类,这正是在 Scitter 实现本身中使用的类型:

  清单 11. 现在我明白了自己为什么喜欢 Scala……

 package com.tedneward.scitter.test;
  import org.junit.*;
  import com.tedneward.scitter.*;
  public class JavaScitterTests
  {
  public static final String testUser = "TESTUSER";
  public static final String testPassword = "TESTPASSWORD";
  @Test public void getFriendsStatuses()
  {
  Scitter scitter = new Scitter(testUser, testPassword);
  if (scitter.verifyCredentials())
  {
  scala.List statuses =
  scitter.friendsTimeline(new scala.collection.mutable.ListBuffer());
  Assert.assertTrue(statuses.length() > 0);
  }
  else
  Assert.assertTrue(false);
  }
  }

  使用返回的 scala.List 不是问题,因为我们可以像对待其他 Collection 类一样对待它(不过我们的确怀念 Collection 的一些优点,因为 List 上基于 Scala 的方法都假设您将从 Scala 中与它们交互),所以,遍历结果并不难,只要用上一点 “旧式” Java 代码(大约 1995 年时候的风格):

  清单 12. 重回 1995,又见 Vector……

     package com.tedneward.scitter.test;
  import org.junit.*;
  import com.tedneward.scitter.*;
  public class JavaScitterTests
  {
  public static final String testUser = "TESTUSER";
  public static final String testPassword = "TESTPASSWORD";
  @Test public void getFriendsStatuses()
  {
  Scitter scitter = new Scitter(testUser, testPassword);
  if (scitter.verifyCredentials())
  {
  scala.List statuses =
  scitter.friendsTimeline(new scala.collection.mutable.ListBuffer());
  Assert.assertTrue(statuses.length() > 0);
  for (int i=0; i 
  {
  Status stat = (Status)statuses.apply(i);
  System.out.println(stat.user().screenName() + " said " + stat.text());
  }
  }
  else
  Assert.assertTrue(false);
  }
  }

  这将我们引向另一个部分,即将参数传递到 friendsTimeline() 方法。不幸的是,ListBuffer 类型不是将一个集合作为构造函数参数,所以我们必须构造参数列表,然后将集合传递到方法调用。这样有些单调乏味,但还可以承受:

  清单 13. 现在可以回到 Scala 吗?

  package com.tedneward.scitter.test;
  import org.junit.*;
  import com.tedneward.scitter.*;
  public class JavaScitterTests
  {
  public static final String testUser = "TESTUSER";
  public static final String testPassword = "TESTPASSWORD";
  // ...
  @Test public void getFriendsStatusesWithCount()
  {
  Scitter scitter = new Scitter(testUser, testPassword);
  if (scitter.verifyCredentials())
  {
  scala.collection.mutable.ListBuffer params =
  new scala.collection.mutable.ListBuffer();
  params.$plus$eq(new Count(5));
  scala.List statuses = scitter.friendsTimeline(params);
  Assert.assertTrue(statuses.length() > 0);
  Assert.assertTrue(statuses.length() == 5);
  for (int i=0; i 
  {
  Status stat = (Status)statuses.apply(i);
  System.out.println(stat.user().screenName() + " said " + stat.text());
  }
  }
  else
  Assert.assertTrue(false);
  }
  }

  所以,虽然 Java 版本比对应的 Scala 版本要冗长一点,但是到目前为止,从任何要使用 Scitter 库的 Java 客户机中调用该库仍然非常简单。好极了。

  结束语

  显然,对于 Scitter 还有很多事情要做,但是它已经逐渐丰满起来,感觉不错。我们设法对 Scitter 库的通信部分进行了 DRY 化处理,并且为 Twitter 提供的很多不同的 API 调用合并了所需的可选参数 — 到目前为止,Java 客户机基本上没有受到我们公布的 API 的拖累。即使 API 没有 Scala 所使用的那些 API 那样干净,但是如果 Java 开发人员要使用 Scitter 库,也不需要费太多力气。

  Scitter 库仍然带有对象的意味,不过我们也开始看到,一些有实用意义的 Scala 特性正在出现。随着我们继续构建这个库,只要有助于使代码更简洁、更清晰,越来越多这样的特性将添加进来。本应如此。


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@ke049m.cn

文章转载自:IT专家网

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP
利记足球官网(官方)网站/网页版登录入口/手机版登录入口-最新版(已更新) 真人boyu·博鱼滚球网(官方)网站/网页版登录入口/手机版登录入口-最新版(已更新) 最大网上PM娱乐城盘口(官方)网站/网页版登录入口/手机版登录入口-最新版(已更新) 正规雷火竞技官方买球(官方)网站/网页版登录入口/手机版登录入口-最新版(已更新) 雷火竞技权威十大网(官方)网站/网页版登录入口/手机版登录入口-最新版(已更新) boyu·博鱼信誉足球官网(官方)网站/网页版登录入口/手机版登录入口-最新版(已更新) 权威188BET足球网(官方)网站/网页版登录入口/手机版登录入口-最新版(已更新) 正规188BET足球大全(官方)网站/网页版登录入口/手机版登录入口-最新版(已更新)