2013-11-13 1 views
0

Я задал вопрос ранее: here, и хотя я принял ответ, я все еще очень далек от понимания, поэтому я немного углубился, и я пишу еще один вопрос.Пожалуйста, помогите мне понять переопределение val в scala

Поведение переоценки val в scala меня удивляет. Например, если этот код:

class A { 
    val name = "AAAAA" 
} 

class B extends A { 
    override val name = "BBBBB" 
} 

, если я говорю:

object Atest extends App { 
    val b = new B 
    println(b.name) 
    val a = b.asInstanceOf[A] 
    println(a.name) 
} 

Я ожидаю

BBBBB 
AAAAA 

, но я получаю

BBBBB 
BBBBB 

Я просто пытаюсь см. значение AAAAA, которое, я думаю, A должно хранить где-то , Поэтому я стараюсь:

class A { 
    val name = "AAAAA" 

    def showSuper { 
    println(name) 
    } 
} 

и:

val b = new B 
    val a = b.asInstanceOf[A] 
    b.showSuper 
    a.showSuper 

, но я все еще получаю:

BBBBB 
BBBBB 

Так что я стараюсь делать взглянуть на то, что Scala фактически генерируя из моих классов:

scalac -Xprint:all A.scala 

дает мне

class A extends Object { 
    private[this] val name: String = _; 
    <stable> <accessor> def name(): String = A.this.name; 
    def <init>(): p3.A = { 
     A.super.<init>(); 
     A.this.name = "AAAAA"; 
    () 
    } 
    }; 
    class B extends p3.A { 
    private[this] val name: String = _; 
    override <stable> <accessor> def name(): String = B.this.name; 
    def <init>(): p3.B = { 
     B.super.<init>(); 
     B.this.name = "BBBBB"; 
    () 
    } 
    } 

Звонок на B.super происходит до того, как B.this.name даже установится, и A четко устанавливает свое имя на AAAAA.

Что происходит? Почему, когда я переопределяю значение val, могу ли я не видеть значение A (или он получает значение B)? Каков механизм, которым это происходит? Как я могу увидеть этот механизм - есть ли фрагмент исходного кода scala, который показывает мне, почему это происходит?

Большое спасибо

EDIT: Предназначенный, чтобы добавить, что если я использую javap взглянуть на байткод, это ясно показывает, что А и В имеют свою собственную копию переменной имя:

$ javap -private A 
Compiled from "A.scala" 
public class p3.A extends java.lang.Object{ 
    private final java.lang.String name; 
    public java.lang.String name(); 
    public p3.A(); 
} 

$ javap -private B 
Compiled from "A.scala" 
public class p3.B extends p3.A{ 
    private final java.lang.String name; 
    public java.lang.String name(); 
    public p3.B(); 
} 

Итак, это не значит, что A и B должны делиться одной и той же переменной - каждый из них может использовать свою собственную копию.

+0

[Этот ответ] (http://stackoverflow.com/questions/12358426/how-to-use-asinstanceof-properly-in-scala) может быть информативным – bwroga

+0

Спасибо, но я не думаю, что это что-то делать с литьем. В моем примере выше, даже b.showSuper, я ожидал бы показать AAAAA, потому что он явно вызывает метод суперкласса, в котором указано поле A (или, по крайней мере, мне кажется, как минимум) – Bruce

ответ

2

Это не Scala специфический. Это вопрос ООП. Второй класс переопределяет метод и скрывает его. Overriding and Hiding Methods

+0

Хм .. что меня озадачивает, так это то, что это касается переменная, а не метод. В java, если вы добавили переменную в свой суперкласс, вы можете получить доступ к полю суперкласса - например ((A) b) .showField1(); и println (((A) b) .field1) различны. Первый по-прежнему вызывает метод подкласса, но второй получает доступ к полю SUPERCLASS. Поэтому, возможно, ответ на мой вопрос заключается в том, что scala рассматривает ВСЕ доступ к полям как метод доступа с правилами переопределения, которые применяются к методам в java, а не к полям? – Bruce

+2

нет. это метод. См. Java-код: public java.lang.String name(); Имя переменной является приватным, имя метода() является общедоступным. – baltov

+0

Ahh ... если я правильно понимаю вас, вы говорите, что даже при том, что я перешел на суперкласс, потому что суперкласс вызывает метод «name», из-за полиморфизма он все равно будет называть метод «name» на подкласс? Это действительно имеет смысл! Большое вам спасибо, это сводило меня с ума. Я также нашел это полезное сообщение http://stackoverflow.com/questions/14912847/interesting-behavior-of-calling-method-after-casting-a-subclass-to-a-super-class с полезным правилом ', когда вы вызовите метод, он будет на типе экземпляра (полиморфизм). Когда вы вызываете переменную, она будет включена. Тип ссылки « – Bruce

1

Просто несколько дополнительных заметок. Выясняется, что если вы декомпилируете классы scala, вы обнаружите, что scala меняет ссылки на vals на ссылки на методы, которые получают vals.Например, когда мой класс А выше, имеет:

def showSuper { 
    println(name) 
} 

декомпилированный байткодом показывает

public void showSuper() 
{ 
    Predef..MODULE$.println(name()); 
} 

(благодаря jd-gui)

Таким образом, это дает понять, что ссылки Scala для вальса эквивалентны в байткод к вызовам полиморфного метода java, а не переменным java, зависящим от типа.

Смежные вопросы