Summa sidvisningar

fredag 2 december 2011

Implicita konverteringar

Tänkte fortsätta på den inslagna vägen att förstå kod genom att titta på den dekompilerade Scala-koden, i det här fallet för att förstå implicit conversions lite bättre. Låt oss ta en titt på följande lilla program:

Exempel 6 - implicit conversions

package nu.tengstrand.scalalab.programminginscala

/**
 * Example of implicit conversion and rich wrappers.
 * Page 94, 212 in the book "Programming in Scala, 2nd edition".
 */
object Example006ImplicitConversion {

  def main(args: Array[String]) {
    println(1 to 3)
    println(4.to(6))
    println(Predef.intWrapper(7).to(9))
   }
}
Kör man programmet får man följande output:
Range(1, 2, 3)
Range(4, 5, 6)
Range(7, 8, 9)
Vi kan konstatera att raderna 10-12 gör samma sak och att de skiljer sig lite på grund av att vi skickat in lite olika argument. Rad 10 utnyttjar att man i Scala kan utelämna punkt och parenteser vid metodanrop. Rad 12 är vad anropen på de andra två raderna översätts till när koden kompileras efter att den applicerat en implicit konvertering.

Läser man raderna 10-12 uppifrån och ned är det ganska uppenbart att läsbarheten minskar för varje rad! Vi får tacka Martin Odersky (skaparen av språket) att vi kan skriva 1 to 3 i stället något annat som skulle kluttra ned koden i onödan!

Låt oss nu ta en titt på den kompilerade koden. Kompilatorn producerar klasserna Example006ImplicitConversion.class och Example006ImplicitConversion$.class, den senare ser ut så här:
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Example006ImplicitConversion.scala

package nu.tengstrand.scalalab.programminginscala;

import scala.Predef$;
import scala.ScalaObject;
import scala.collection.immutable.Range;
import scala.runtime.RichInt;

public final class Example006ImplicitConversion$ implements ScalaObject {

 public void main(String args[]) {
  Predef$.MODULE$.println(Predef$.MODULE$.intWrapper(1).to(3));
  Predef$.MODULE$.println(Predef$.MODULE$.intWrapper(4).to(6));
  Predef$.MODULE$.println(Predef$.MODULE$.intWrapper(7).to(9));
 }

 private Example006ImplicitConversion$() {
 }

 public static final Example006ImplicitConversion$ MODULE$ = this;

 static {
  new Example006ImplicitConversion$();
 }
}

Hur hänger då detta ihop. Allt som ligger i klassen scala.Predef inkluderas när man kompilerar (på liknande sätt som java.lang.* inkluderas i Java). Klassen Predef ärver från LowPriorityImplicits som i sin tur har följande implicit deklarerad:
implicit def intWrapper(x: Int) = new runtime.RichInt(x)
När kompilatorn upptäcker att datatypen Int saknar metoden to börjar den leta efter implicita konverteringar och hittar då intWrapper som den använder för att wrappa våran Int och får då tillgång till dennes to-metod.

Det var ju inte så svårt det här!

Inga kommentarer:

Skicka en kommentar