六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 90|回复: 0

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

[复制链接]

升级  48%

6

主题

6

主题

6

主题

童生

Rank: 1

积分
24
 楼主| 发表于 2013-1-28 16:44:27 | 显示全部楼层 |阅读模式
刚写了一个小应用,其中有个从文件读取数据转换后存入数据库的功能,代码见https://github.com/itang/_demo/blob/master/pagingtree/src/main/scala/pagingtree/plugins/DataPlugin.scala, 在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有什么诡异的实现?
   
    没办法,掉个方向,看看scala.io.Codes源码里有什么玄机没。下面这几行代码引起了我的注意:
      trait LowPriorityCodecImplicits {        self: Codec.type =>        /** The Codec of Last Resort. */        implicit def fallbackSystemCodec: Codec = defaultCharsetCodec    }    object Codec extends LowPriorityCodecImplicits {        ...        def defaultCharsetCodec   = apply(Charset.defaultCharset)        ...    }
   
    LowPriorityCodecImplicits 这个trait里声明了Codes的隐式参数值fallbackSystemCodec,而Codes对象继承了LowPriorityCodecImplicits
    但是感觉有点不对劲,Source源码里应该导入它啊,就如:
      import Codes._
   
    猜想: 难道类的半生对象里定义了其类型的隐式参数值, 不需要显式导入就可以被隐式引用吗?
   
    带着这个疑问, 写了如下一些代码来验证之:
    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;    }

    答案浮出水面了, ubuntu下 文件编码默认为UTF-8, windows xp下是GBK。这就是fromInputStream(is: InputStream)(implicit codec: Codec)第二个参数使用隐式值之后造成的后果
   
    前前后后的分析、猜想论证,总结出以下几条

  • scala.io.Source.fromInputStream(包括fromFile等)等方法第二个参数最好显显式的指定编码, 这样才不会受系统环境差异的影响    如Source.fromURL(new URL("http://www.baidu.com"), "GBK")
  • 如果类的半生对象里定义了其类型的隐式参数值, 那么此隐式参数值不需要显式导入就可以被引用到
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表