Implicit parameters 使用问题一,从Source.fromInputStream说起

刚写了一个小应用,其中有个从文件读取数据转换后存入数据库的功能,代码见[url]https://github.com/itang/_demo/blob/master/pagingtree/src/main/scala/pagingtree/plugins/DataPlugin.scala[/url], 在ubuntu下运行正常,windows下出现编码问题,编码为utf-8的文件读取不了。

定位到的出错语句是:
 val regionData = fromLines(Source.fromInputStream(RegionDataImporter.getClass.getResourceAsStream("/data/region.txt")).getLines.toList

注意到Source.fromInputStream方法, 我这使用的应该是
def fromInputStream(is: InputStream)(implicit codec: Codec): BufferedSource

(fromInputStream另外一个定义是: def fromInputStream(is: InputStream, enc: String): BufferedSource)

显然第二个参数使用了隐式参数, 由此猜想这个隐式参数值跟平台有关。要弄清楚, 只有翻scala源码了。

按常理,先找到Source的源码, 猜想在某处导入了 implicit defaultCodec = Codec.xxx 之类。意外的是Source里未定义它,而且也未通过import XX._ 导入。

进一步判断是不是在scala.Predef 全局导入了。查找了Predef的源码,也未发现。有点诧异。难道scala有什么诡异的实现?

没办法,掉个方向,看看[b]scala.io.Codes[/b]源码里有什么玄机没。下面这几行代码引起了我的注意:
  trait LowPriorityCodecImplicits {
self: Codec.type =>

/** The Codec of Last Resort. */
implicit def fallbackSystemCodec: Codec = defaultCharsetCodec
}

object Codec extends LowPriorityCodecImplicits {
...
def defaultCharsetCodec = apply(Charset.defaultCharset)
...
}


[b]LowPriorityCodecImplicits 这个trait里声明了Codes的隐式参数值fallbackSystemCodec,而Codes对象继承了LowPriorityCodecImplicits[/b]。
但是感觉有点不对劲,Source源码里应该导入它啊,就如:
import Codes._


猜想: [b]难道类的半生对象里定义了其类型的隐式参数值, 不需要显式导入就可以被隐式引用吗?[/b]

带着这个疑问, 写了如下一些代码来验证之:
test.scala
-------------------
   class A { override def toString = "A"}
trait T {
implicit def a = new A
}
object A extends T { } // T.a 混入 object A
def a(implicit pa: A) = pa

println(a) // 打印出 "A"


其效果跟如下代码是一样的:

    class A { override def toString = "A"}
implicit def a = new A
def a(implicit pa: A) = pa
println(a) // 打印出 "A"



算是知道这么回事了(还是觉得scala如此处理有点诡异),那编码问题的密码完全归结到java.nio.charset.Charset.defaultCharset方法了,看其源码:

public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
//从系统属性里读出file.encoding配置的值
//即System.getProperty("file.encoding");
String csn = AccessController.doPrivileged(
new GetPropertyAction("file.encoding"));
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}


答案浮出水面了, [b]ubuntu下 文件编码默认为UTF-8, windows xp下是GBK。这就是fromInputStream(is: InputStream)(implicit codec: Codec)第二个参数使用隐式值之后造成的后果[/b]。

[color=blue][b]前前后后的分析、猜想论证,总结出以下几条[/b][/color]:
[list]
[*][b]scala.io.Source.fromInputStream(包括fromFile等)等方法第二个参数最好显显式的指定编码, 这样才不会受系统环境差异的影响[/b]
如Source.fromURL(new URL("http://www.baidu.com"), "GBK")
[*][b]如果类的半生对象里定义了其类型的隐式参数值, 那么此隐式参数值不需要显式导入就可以被引用到[/b]
[/list]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值