3. For comprehensions
Syntactic sugar
For
В Scala for является синтаксическим сахаром, тем не
comprehensions
By-name
менее правила преобразования довольно сложны
parameters
Non local return Один генератор с ’yield’ тоже, что вызов метода ’map’
Local functions
Lazy values Один генератор без ’yield’ тоже, что ’foreach’
Generated Несколько генераторов транслируются во ’flatMap’
ByteCode
Binary
compatibility
Questions? f o r ( i <− 1 t o 5) (1 to 5) . map { i = i > i }
yield i i (1 to 5) . f o r e a c h {
f o r ( i <− 1 t o
println ( i )
5) }
i = println ( i )
>
f o r { i <− 1 t o 5 (1 to 5) . f l a t M a p {
j <− 1 t o 5} i = (1 to 5) . map { j = i + j }
> >
yield i + j }
Подробнее можно почитать в спецификации.
3 / 19
4. By-name parameters
Syntactic sugar
For
Возможность в вызов метода передавать выражения
comprehensions
By-name
лениво это другой пример того, где неявно могут быть
parameters
Non local return
зашиты closures. Следующие два примера идентичны:
Local functions
Lazy values
d e f foo ( x : => Int ) { d e f foo ( x : ( ) => Int ) {
Generated
ByteCode println ( x ) println ( x ( ) )
} }
Binary
compatibility
foo ( 1 ) foo ( ( ) => 1 )
Questions?
Что изредка может приводить к ошибке (параметр будет
вычисляться каждый раз, когда к нему будет
производится обращение).
4 / 19
5. Non local return
Syntactic sugar
For
Return statement внутри closure на деле компилируется в
comprehensions
By-name
код, который бросает NonLocalReturnControl. В
parameters
Non local return
большинстве случаев это удобно, но нужно учитывать
Local functions следующее
Lazy values
Generated В критичных по производительности кусках кода этот
ByteCode
Binary
exception следует избегать
compatibility
Нельзя ловить Throwable в том месте, где этот
Questions?
exception бросается
В силу синтаксического сахара описанного ранее, все
это может происходить неявно (for-comprehensions и
by-name parameters)
5 / 19
6. Local functions
Syntactic sugar
For
В Java любой вспомогательный код оформляется в виде
comprehensions
By-name
private методов на уровне класса. В Scala это можно
parameters
Non local return
сделать более структурированным, если использовать
Local functions локальные методы, хотя на уровне bytecode получается
Lazy values
Generated
примерно тоже самое:
ByteCode
Binary
compatibility import scala . runtime . IntRef ;
public class LocalFunctions {
Questions? class LocalFunctions {
p u b l i c void foo () {
def foo {
I n t R e f i $ 1 = new I n t R e f ( 1 ) ;
var i = 1
i n t j$1 = 2;
val j = 2
l o c a l $ 1 ( i$1 , j$1 ) ;
def local {
i += j
l o c a l $ 1 ( i$1 , j$1 ) ;
}
}
p r i v a t e f i n a l void local$1 (
local
IntRef intref , i n t i ) {
local
i n t r e f . e l e m += i ;
}
}
}
p u b l i c L o c a l F u n c t i o n s ( ) {}
}
6 / 19
7. Lazy values
Syntactic sugar
For
Это тоже синтаксический сахар, который надо
comprehensions
By-name
использовать аккуратно (так как возможны deadlock и
parameters
Non local return
проблемы производительности). Независимо от
Local functions расположения lazy val, компилируется он таким образом
Lazy values
Generated
ByteCode
import scala . runtime . BoxedUnit ;
Binary p u b l i c c l a s s Lazy {
compatibility public int x () {
i f ( ( b i t m a p $ 0 & 2 ) == 0 )
Questions? synchronized ( this ) {
i f ( ( b i t m a p $ 0 & 2 ) == 0 ) {
x = 1;
bitmap$0 = bitmap$0 | 2;
c l a s s Lazy {
lazy val x = 1
}
BoxedUnit _tmp =
}
BoxedUnit . UNIT ;
}
return x ;
}
p u b l i c L a z y ( ) {}
private int x ;
public v o l a t i l e i n t bitmap$0 ;
}
7 / 19
10. Objects
Syntactic sugar
Объект всегда компилируется в два класса.
Generated
ByteCode
Classes and names
Object$ содержит в себе статическое поле MODULE$,
Objects а также код всех методов
Traits
Trait subclasses Object содержит в себе все статические методы
Linearization
Initialisation order объекта, с телом вида Object$.MODULE$.foo()
Binary
compatibility Важно понимать, что статические методы добавлены для
Questions? удобства вызова из Java. Тем не менее они добавляются
не всегда.
В Companion Trait не добавляется ничего.
В Companion Class не добавляются методы с таким
именем, которое уже есть в этом классе
Методы класса java.lang.Object
10 / 19
11. Traits
Syntactic sugar
Если trait отличается от обычного интерфейса в Java
Generated
ByteCode наличием конкретных методов, то к соответствующему
Classes and names
Objects интерфейсу будет создан класс Trait$class с
Traits
Trait subclasses
имплементацией этих методов.
Linearization
Initialisation order
Binary public abstract class A$class {
compatibility public static int foo ( A $this ,
int x) {
Questions? return 2;
trait A {
}
val x = 1 public s t a t i c void $init$ (
A $this
def foo ( x : Int ) = 2
) {
}
$this .
test$A$_setter_$x_$eq (1) ;
}
}
11 / 19
12. Trait subclasses
Syntactic sugar
Если в Java попытаться реализовать Trait, в котором есть
Generated
ByteCode уже имплементированые методы, то их все равно
Classes and names
Objects пришлось бы реализовывать. Другой вопрос, что это
Traits
Trait subclasses
можно сделать предельно легко:
Linearization
Initialisation order
Binary i n t foo ( ) { r e t u r n Trait$class . foo ( t h i s ) ; }
compatibility
Questions?
В случае наследников написанных на Scala, то же самое
делает компилятор.
12 / 19
13. Linearization
Syntactic sugar
Этот алгоритм требуется компилятору, чтобы определить,
Generated
ByteCode в каком порядке надо искать методы в базовых классах.
Classes and names
Objects
Traits
Trait subclasses trait A extends B
Linearization trait B
Initialisation order class C extends B
Binary class D extends C with A
compatibility
Questions?
Здесь порядок D, A, C, B.
Если два метода с одинаковой сигнатурой без
модификатора ’override’ приходят в класс наследник из
супертрейтов, то ошибки не будет, так как обязательно
надо будет переопределить эту сигнатуру. Если же у
кого-то есть модификатор ’override’, может случится
неявный override согласно правилам линеаризации.
13 / 19
14. Initialisation order
Syntactic sugar
Инициализация происходит в порядке, обратном
Generated
ByteCode линеаризации. Если к переменной обратиться до того, как
Classes and names
Objects она проинициализирована, можно получить NPE.
Traits
Trait subclasses
Есть два способа избавится от подобных проблем:
Linearization
Initialisation order Использовать lazy val
Binary
compatibility
Использовать early definitions
Questions?
14 / 19
16. Methods
Syntactic sugar
Здесь все очень похоже на Java. Чаще всего проблем не
Generated
ByteCode будет.
Binary
compatibility Добавлять метод можно даже в Trait, но только если
Methods
Values старые методы не ссылаются на новые, так как
Lazy values
имплементации в классы наследники добавлены не
Questions?
будут.
В класс опасно добавлять методы, имена которых
совпадают с именем методов из Companion Object, так
как статические реализации будут удалены
компилятором, что может вызвать проблемы с кодом
написанным не на Scala.
16 / 19
17. Values
Syntactic sugar
Если переменная находится в классе или объекте, то
Generated
ByteCode переменная компилируется в getter, setter (для variables) и
Binary
compatibility
field, который инциализируется в конструкторе класса. А
Methods значит все будет также хорошо как и с методами.
Values
Lazy values Если переменная находится в Trait, то она компилируется
Questions? в getter и setter (всегда). Инициализация происходит в
методе Trait$class.$init$ через вызов setter.
Значит добавлять переменную в трейт нельзя. Так как
будет вызван абстрактный setter.
По аналогичным причинам нельзя изменять def на val
Тем не менее val на def также изменять нельзя, так как
тогда в классах наследниках, где как раз и хранится
field, переменная не будет проинициализирована.
17 / 19
18. Lazy values
Syntactic sugar
В подклассах не генерируется field bitmap, если он есть в
Generated
ByteCode базовом классе.
Binary
compatibility Поэтому в этих случаях добавлять lazy value в классы
Methods
Values нельзя
Lazy values
В Trait добавлять можно, если эта переменная не
Questions?
будет использована в старом коде
Так как компиляция lazy val в Trait от компиляции
метода не отличается совсем, то их можно заменять
друг на друга, учитывая только, что в наследниках
подобная семантика не изменится
18 / 19