回答一个新的问题:为什么class A { type T }
里面的T未定义,表示某种抽象类型,而A却不需要声明为abstract,这是为何?
scala> class A { type T }
defined class A
scala> val a =new A
a: A = A@69284047
确实,尽管T类型未声明,却并不影响A的实例化。其实,这种写法,跟 class A[T] {}
效果上是相似的,都是定义了一个泛型参数。不过我们看看它们在类型推断上的差异:
scala> class A[T] { def foo(p:T) {println(p)} }
defined class A
scala> new A
res3: A[Nothing] = A@49bb2f1c
scala> (new A).foo("hi")
hi
上面把T声明为泛型参数(type parameter)的话,构造的时候不给出具体参数,编译器会推导为A[Nothing]。
scala> class B { type T; def foo(p:T) {println(p)} }
defined class B
scala> new B
res4: B = B@72f1b266
scala> b.foo("hi")
<console>:9: error: type mismatch;
found : String("hi")
required: b.T
(new B).foo("hi")
^
而在class内部定义一个抽象类型T,创建实例时不指定具体类型时,这个类型是一个抽象的路径依赖类型”b.T”,要想满足的话,需要显式的声明一个这样的参数传入:
scala> val p: b.T = "hi".asInstanceOf[b.T]
p: b.T = hi
scala> b.foo(p)
hi
能对asInstanceOf的语意做个深入的介绍么?
val p: b.T = “hi”.asInstanceOf[b.T]
就这句来说, scala真是有点神奇, b.T还是个未”实现“类型(抽象类型), 居然可以:
1、赋值
2、”hi”可以被cast为b.T这一个抽象类型。
哪里可以看到asInstanceOf的源代码? 应该是在Any类实现的把, scala源码丽没找到Any类的源码。
asIsntanceOf我想主要用法是:
scala> class A; class B extends A
defined class A
defined class B
scala> val a: A = new B
a: A = B@58ab0523
scala>
scala> val b = a.asInstanceOf[B]
b: B = B@58ab0523
但是下面又是可以的。
scala> val t = 2.0d.asInstanceOf[Int]
t: Int = 2
看来asInstanceOf与java的下列用法与语意还是不同的。
public class T {
public static void main(String[] args) {
Object o = new String(“hello world”);
String s = (String)o;
System.out.println(s);
}
}
2.0d.asInstanceOf[Int]实际是使用了implicit的,参看这个:http://stackoverflow.com/a/12359801/2073130
你的结论是?
你转的stack我看了, 这里面并没有解答这个问题吧。
第一位说: 对于Numerical类型, x.asInstanceOf[T] 都作为conversion来处理
第二位不认同这个观点: 他认为conversion casting是两个概念。 asInstanceOf是casting. 他认为casting只能在一个类型树下进行, 如果不能casting就应该抛出exception.
所以你的观点与结论是?
前面我说的武断了,这里的conversion不是通过implicit来做的,而是在运行时调用toT方法。asInstanceOf的意义在于支会编译器,告诉它一条额外的类型信息。至于具体是属于casting还是conversion都无所谓,真正要求的是参与的两个类型之间有明确的变换路径。有人认为asInstanceOf应该只服务于casting,但这样一来数值类型的转换又会需要一个额外的util来处理,相比共用asInstanceOf这么做明显多余。
asInstanceOf 方法是 Any.scala里定义的,它是运行时才能确定的。
/** Cast the receiver object to be of type `T0`.
*
* Note that the success of a cast at runtime is modulo Scala’s erasure semantics.
* Therefore the expression `1.asInstanceOf[String]` will throw a `ClassCastException` at
* runtime, while the expression `List(1).asInstanceOf[List[String]]` will not.
* In the latter example, because the type argument is erased as part of compilation it is
* not possible to check whether the contents of the list are of the requested type.
*
* @throws ClassCastException if the receiver object is not an instance of the erasure of type `T0`.
* @return the receiver object.
*/
def asInstanceOf[T0]: T0 = sys.error(“asInstanceOf”)
val p: b.T = “hi”.asInstanceOf[b.T]
这句能够成功的依据是什么。 总觉得怪怪得,b.T还是个未实现的类型啊?
对于变量v, 和类型T.
v.asInstanceOf[T] 能够否成功的判断依据是么?
你想多了。
相对于具体的对象实例来说,类型本来就是抽象的,路径依赖类型只是做了一下限定,跟java里的内部类(非静态)的限定类似,只是可以更细粒度,比如限制这个类型必须依赖某一个特定对象实例(参考路径依赖类型)。
v.asInstanceOf[T] 就是判断v实例在构造时的类型X,是否是 T 的子类型(或T本身)而已
如果:
v.asInstanceOf[T] 就是判断v实例在构造时的类型X,是否是 T 的子类型(或T本身)而已
那么, 就这个例子而言:
val p: b.T = “hi”.asInstanceOf[b.T]
v = “hi”
T = b.T
X = String
也就是说 String是b.T的子类型 或String类型就是b.T类型
但是b.T类型并没有定义啊?
再者:
scala> 1.0.asInstanceOf[Int]
res195: Int = 1
Float也并非Int的子类型。
对于某些基础类型,是可以相互造型的,Int, Long, Double, Float 之间可以相互造型,只是可能存在精度丢失。
对于引用类型,判断的依据仍是我说的那样。就这个问题而言,这里 b.T 是一个抽象类型,在类型推导的时候,被推导成了一个非常泛的类型,最终在字节码层面是一个Object类型,所以把String造型过去也没有问题。
% cat A.scala
class B { type T; def foo(p:T) {println(p)} }
object Main{
val b = new B
val p: b.T = “hi”.asInstanceOf[b.T]
}
% scalac -Xprint:jvm A.scala
object Main extends Object {
def b(): B = Main.this.b;
def p(): Object = Main.this.p;(): Main.type = { ();
private[this] val b: B = _;
private[this] val p: Object = _;
def
Main.super.
Main.this.b = new B();
Main.this.p = “hi”;
()
}
}
你可能弄混了“运行时”,与“编译时”,b.T 在编译时,被编译器推导它最终的类型,之后在运行时是明确的。至于这个类型在编译阶段的推导过程,可以通过
scalac -Y:debug-typer A.scala 来看一下,这个路径依赖类型的推导过程非常复杂(里面还有adapte的尝试),最终被适配为了Any,这涉及到scala背后类型推导的实现机制,就不去细究了。
有道理!
补充一篇好文,martin对泛型与抽象类型的对比和解释:
https://github.com/wecite/papers/blob/master/An-Overview-of-the-Scala-Programming-Language/5.Abstraction.md#53–%E7%94%A8%E6%8A%BD%E8%B1%A1%E7%B1%BB%E5%9E%8B%E5%BB%BA%E7%AB%8B%E6%B3%9B%E5%9E%8B%E6%A8%A1%E5%9E%8Bmodeling-generics-with-abstract-types
摘自里面的内容:
两种抽象模式之间可以转换,对于一种语言还是有价值的,因为可以降低其内在的概念复杂性。例如,Scala 的泛型,实际上就是一种语法糖,完全可以被抽象类型替代掉。既然如此,也许会有人问,这种语法糖有没有必要性?或者说为什么不只用抽象类型呢,这样可以使语法本身简化很多。实际上,Scala 中引入泛型有两重意义:首先,手工把泛型转化为成为抽象类型表达形式并不那么简单,不仅会丧失语法的简洁性,而且还可能带来前述的命名冲突等问题。其次,泛型和抽象类型在 Scala 中一般扮演不同的角色,泛型一般用于类型的实例化,而抽象类型主要用于在调用者代码中对相应的抽象类型进行引用。后者主要来自于两个场合:一个是有人需要在客户代码中隐藏相关类型信息,用于构造类似于SML模式的模块系统。另一个是在子类中协变地继承父类的类型,从而获得族多态。
可能有人会问,那么是否可以反过来用泛型来替代抽象类型呢?一些对于两种抽象方式都支持的系统进行的研究 [27] 证实,这样做要困难得多,至少整个程序都需要重写。不仅如此,如果系统要实现受限多态的话,重写类型上/下界的部分会呈平方级增长 [8]。实际上这一点也不奇怪,因为这两种类型体系的理论基础就不同,泛型(不带 F-界的)可以用 F<: 系统来表达 [11],而抽象类型则建立在类型依赖的基础之上。后者比前者的表现力更强,例如,带路径依赖类型的 νObj 演算是可以涵盖 F<: 的。
我觉得在非抽象类中出现抽象类型,可能是个Scala的bug
我不好评价,或许作者另有考虑