2. Scopes
Scopes - это место в коде, где может быть
объявлена переменная.
● { - начинает новый scope, кроме классов
● Каждый генератор в for statement
начинает новый scope
● Функция начинает новый scope для
параметров
● Case clause начинает новый scope
Переменная объявленная внутри одного
scope не видна снаружи.
3. Names shadowing
Во внутреннем scope можно скрывать имя
определенное во внешнем scope при
условии, что приоритет этого имени не ниже
чем у имени объявленного снаружи.
Это ошибка объявить переменные с одним
именем внутри одного и того же scope.
Существует два вида имен: expressions, types.
8. Early definitions
Если нужно определить что-то до вызова
super конструктора, это можно сделать в
early definitions:
class B(x: Int)
class A(s: String) extends {
val i = s.toInt
} with B(i * i)
9. Initialization (early defs)
object InitializationNPE {
class A {
val x1 = "text"
val x2 = x1.length
}
class B extends {
override val x1 = "text1"
} with A
def main(args: Array[String]) {
new B
}
}
10. Lazy values
Scala поддерживает синтиаксис ленивых
вычислений. От пользователя требуется
лишь объявить переменную, как lazy.
Реализация double-checked locking, что
приводит к определенным сложностям.
11. Initialization (lazy vals)
object InitializationNPE {
class A {
lazy val x1 = "text"
val x2 = x1.length
}
class B extends A {
override lazy val x1 = "text1"
}
def main(args: Array[String]) {
new B
}
}
12. Few words about traits
Как же компилируются traits, рассмотрим
простой пример:
trait Base {
def foo() = 1
}
class Child extends Base
13. Few words about traits
public interface Base {
int foo();
}
public class Base$class {
public static int foo(Base base) {
return 1;
}
}
public class Child implements Base {
public int foo() {
return Base$class.foo(this);
}
}
14. Embedded XML
Практически любой валидный XML можно
использовать как литерал:
val xml1 = <a>Some text<b/></a>
val xml2 = <a>{3 + 4}</a>
val xml3 = <a>{"</a>It's just String<a>"}</a>
Если надо написать {, то его можно
заэскейпить {{.
15. XML API
Базовый класс scala.xml.Node.
Доступ к subelement через “name”.
К аттрибутам через “@name”.
Рекурсивный поиск в глубину “name”.
val xml = <a id="text"><b>S<c></c></b></a>
xml.text // S
xml "b" // <b>S<c></c></b>
xml "@id" // text
xml "c" // <c></c>
16. Using XML
Встроенный XML легко использовать для
сериализации:
val node = <x></x>
scala.xml.XML.save("File.xml", node)
scala.xml.XML.load("File.xml")
17. Pattern matching
В pattern matching есть особый синтаксис
для XML:
val node = <x></x>
scala.xml.XML.save("File.xml", node)
scala.xml.XML.load("File.xml")
21. Primitive types
Есть несколько важных фич:
● Numeric Widening. Если работает weak
conformance, то он автоматический.
● Literal Narrowing. Literal с ожидаемым
типом Byte, Short or Char конвертируется.
● Value discarding. Если тип Uеnit, то для
любого выражения, есть конверсия.
Value classes также наследуются от AnyVal.
22. Type designators
Это просто любой class, trait или object как
тип. Ничего особенного здесь не
подразумевается.
23. Parameterized types
Для type designator и его type parameters есть
два правила:
● Число type parameters должно of underli
совпадать с числом параметорв
соответствующего класса type designator.
● Каждый type parameter должен подходить
к type parameter bounds
24. Tuple types
Синтаксический сахар для TupleN[T1, ..., Tn]
типа. Subtyping разрешается также, как и
subtyping соответствующего TupleN класс
согласно вариантности.
Поэтому этот код и не скомпилируется:
object A {
def foo(x: (Int, Int)) = x._1 + x._2
def foo(x: (String, String)) = x._1 + x._2
}
25. Automatic tupling
Позволяет избежать лишних скобок,
особенно это полезно для infix нотации.
object A {
def foo(x: (Int, Int)) = x._1 + x._2
}
A.foo((1, 2))
A.foo(1, 2)
A foo (1, 2)
println(1, 2, 3)
26. Function type
Уже подробно изученный нами
синтаксический сахар. В силу недосмотра в
компиляторе это несовсем синтаксический
сахар:
val x1: Function1[Int, Int] = x => x + 1
val x2: Int => Int = _ + 1
val y1: Function1[Seq[Int], Int] = x => x.length
val y2: (Int*) => Int = x => x.length
27. Annotated types
Любой тип можно проаннотировать. С точки
зрения системы типов эта аннотация будет
проигнорирована:
class A
val a: A @serializable = new A
29. Infix types
Может быть полезно для операторов над
типами:
type n[A] = A => Nothing
type ++[T, U] = n[n[T] with n[U]]
type nn[A] = n[n[A]]
type |+|[T, U] = { type λ[X] = nn[X] <:< (T ++ U) }
def size[T : (Int |+| String)#λ] (t: T) = t match {
case i: Int => i
case s: String => s.length
}