Skip to content

Russian Tour full #1263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _data/translations.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
tour:
languages: [ba, es, ko, pt-br, pl, zh-cn, th]
languages: [ba, es, ko, pt-br, pl, zh-cn, th, ru]
77 changes: 77 additions & 0 deletions _ru/tour/abstract-type-members.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
layout: tour
title: Члены Абстрактного Типа

discourse: true

partof: scala-tour
num: 23
language: ru
next-page: compound-types
previous-page: inner-classes
topics: abstract type members
prerequisite-knowledge: variance, upper-type-bound

---

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

```tut
trait Buffer {
type T
val element: T
}
```
Здесь мы определили абстрактный тип `T`, который используется для описания типа члена `element`. Мы можем расширить его в абстрактном классе, добавив верхнюю границу нового типа `U` связанного с `T`, делая описание типа более конкретным.

```tut
abstract class SeqBuffer extends Buffer {
type U
type T <: Seq[U]
def length = element.length
}
```
Обратите внимание, как мы можем использовать новый абстрактный тип `U` в качестве верхней границы типа. Класс `SeqBuffer` позволяет хранить в буфере только последовательности, указывая, что тип `T` должен быть подтипом `Seq[U]` для нового абстрактного типа `U`.

[Трейты](traits.html) или [классы](classes.html) с членами абстрактного типа часто используются в сочетании с анонимными экземплярами классов. Чтобы проиллюстрировать это рассмотрим программу, которая имеет дело с буфером, который ссылается на список целых чисел:

```tut
abstract class IntSeqBuffer extends SeqBuffer {
type U = Int
}


def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer =
new IntSeqBuffer {
type T = List[U]
val element = List(elem1, elem2)
}
val buf = newIntSeqBuf(7, 8)
println("length = " + buf.length)
println("content = " + buf.element)
```
Здесь класс `newIntSeqBuf` создает экземпляры `IntSeqBuffer`, используя анонимную реализацию класса `IntSeqBuffer` (т.е. `new IntSeqBuffer`), устанавливая тип `T` как `List[Int]`.

Мы можем вывести тип класса из типа его членов и наоборот. Приведем версию кода, в которой выводится тип класса из типа его члена:

```tut
abstract class Buffer[+T] {
val element: T
}
abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] {
def length = element.length
}

def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] =
new SeqBuffer[Int, List[Int]] {
val element = List(e1, e2)
}

val buf = newIntSeqBuf(7, 8)
println("length = " + buf.length)
println("content = " + buf.element)
```

Обратите внимание, что здесь необходимо использовать [вариантность в описании типа](variances.html) (`+T <: Seq[U]`) для того, чтобы скрыть конкретный тип реализации списка, возвращаемого из метода `newIntSeqBuf`.
129 changes: 129 additions & 0 deletions _ru/tour/annotations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
layout: tour
title: Аннотации

discourse: true

partof: scala-tour

num: 32
language: ru
next-page: default-parameter-values
previous-page: by-name-parameters

---

Аннотации используются для передачи метаданных при объявлении. Например, аннотация `@deprecated` перед объявлением метода, заставит компилятор вывести предупреждение, если этот метод будет использован.
```
object DeprecationDemo extends App {
@deprecated("deprecation message", "release # which deprecates method")
def hello = "hola"

hello
}
```
Такой код скомпилируется, но компилятор выдаст предупреждение: "there was one deprecation warning".

Аннотация применяется к первому идущему после нее объявлению или определению. Допускается использование сразу нескольких аннотаций следующих друг за другом. Порядок, в котором приводятся аннотации, не имеет значения.

## Аннотации, обеспечивающие корректность работы кода
Некоторые аннотации приводят к невозможности компиляции, если условие (условия) не выполняется. Например, аннотация `@tailrec` гарантирует, что метод является [хвостовой рекурсией](https://ru.wikipedia.org/wiki/%D0%A5%D0%B2%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%B0%D1%8F_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F). Хвостовая рекурсия помогает держать потребление памяти на постоянном уровне. Вот как она используется в методе, который вычисляет факториал:

```tut
import scala.annotation.tailrec

def factorial(x: Int): Int = {

@tailrec
def factorialHelper(x: Int, accumulator: Int): Int = {
if (x == 1) accumulator else factorialHelper(x - 1, accumulator * x)
}
factorialHelper(x, 1)
}
```
Метод `factorialHelper` имеет аннотацию `@tailrec`, которая гарантирует, что метод действительно является хвостовой рекурсией. Если бы мы изменили реализацию `factorialHelper` так как указано далее, то компиляция бы провалилась:
```
import scala.annotation.tailrec

def factorial(x: Int): Int = {
@tailrec
def factorialHelper(x: Int): Int = {
if (x == 1) 1 else x * factorialHelper(x - 1)
}
factorialHelper(x)
}
```
Мы бы получили сообщение "Recursive call not in tail position"(Рекурсивный вызов не в хвостовой позиции).


## Аннотации, влияющие на генерацию кода
Некоторые аннотации типа `@inline` влияют на сгенерированный код (т.е. в результате сам код вашего jar-файл может отличаться). Такая аннотация означает вставку всего кода в тело метода вместо вызова. Полученный байт-код длиннее, но, надеюсь, работает быстрее. Использование аннотации `@inline` не гарантирует, что метод будет встроен, но заставит компилятор сделать это, если и только если будут соблюдены некоторые разумные требования к размеру сгенерированного кода.

### Java аннотации ###
Есть некоторые отличий синтаксиса аннотаций, если пишется Scala код, который взаимодействует с Java.

**Примечание:**Убедитесь, что вы используете опцию `-target:jvm-1.8` с аннотациями Java.

Java имеет определяемые пользователем метаданные в виде [аннотаций](https://docs.oracle.com/javase/tutorial/java/annotations/). Ключевой особенностью аннотаций является то, что они задаются в виде пар ключ-значение для инициализации своих элементов. Например, если нам нужна аннотация для отслеживания источника какого-то класса, мы можем определить её как

```
@interface Source {
public String URL();
public String mail();
}
```

А затем использовать следующим образом

```
@Source(URL = "http://coders.com/",
mail = "[email protected]")
public class MyClass extends HisClass ...
```

Использование аннотации в Scala похоже на вызов конструктора. Для создания экземпляра из Java аннотации необходимо использовать именованные аргументы:

```
@Source(URL = "http://coders.com/",
mail = "[email protected]")
class MyScalaClass ...
```

Этот синтаксис достаточно перегруженный, если аннотация содержит только один элемент (без значения по умолчанию), поэтому, если имя указано как `value`, оно может быть применено в Java с помощью конструктора-подобного синтаксиса:

```
@interface SourceURL {
public String value();
public String mail() default "";
}
```

А затем можно использовать следующим образом

```
@SourceURL("http://coders.com/")
public class MyClass extends HisClass ...
```

В этом случае Scala предоставляет такую же возможность

```
@SourceURL("http://coders.com/")
class MyScalaClass ...
```

Элемент `mail` был указан со значением по умолчанию, поэтому нам не нужно явно указывать его значение. Мы не можем смешивать эти два стиля в Java:

```
@SourceURL(value = "http://coders.com/",
mail = "[email protected]")
public class MyClass extends HisClass ...
```

Scala обеспечивает большую гибкость в этом отношении

```
@SourceURL("http://coders.com/",
mail = "[email protected]")
class MyScalaClass ...
```
63 changes: 63 additions & 0 deletions _ru/tour/automatic-closures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
layout: tour
title: Конструкция Автоматического Замыкания Зависимого Типа

discourse: true
language: ru
partof: scala-tour
num: 14
---

Scala допускает использование в качестве параметров методов имена беспараметрических функций. При вызове такого метода фактические параметры для беспараметрических функций не вычисляются, а передается функция с нулем аргументов, которая захватывает вычисление соответствующего параметра (так называемый *вызов по имени*).

Следующий код демонстрирует этот механизм:

object TargetTest1 extends Application {
def whileLoop(cond: => Boolean)(body: => Unit): Unit =
if (cond) {
body
whileLoop(cond)(body)
}
var i = 10
whileLoop (i > 0) {
println(i)
i -= 1
}
}

Функция whileLoop принимает два параметра `cond` и `body`. При использовании функции значения этих параметров не вычисляются. Но всякий раз, когда параметры используются в теле `whileLoop`, их значение будет вычисляться заново через использование автоматически созданных неявно вызываемых функций. Таким образом, наш метод `whileLoop` реализует Java-подобный цикл while-loop со схемой рекурсивной реализации.

Мы можем комбинировать использование [инфиксных/постфиксных операторов](operators.html) с этим механизмом для создания более сложных выражений (с хорошим синтаксисом).

Вот реализация loop-unless выражения:

object TargetTest2 extends Application {
def loop(body: => Unit): LoopUnlessCond =
new LoopUnlessCond(body)
protected class LoopUnlessCond(body: => Unit) {
def unless(cond: => Boolean) {
body
if (!cond) unless(cond)
}
}
var i = 10
loop {
println("i = " + i)
i -= 1
} unless (i == 0)
}
Функция `loop` принимает только тело цикла и возвращает экземпляр класса `LoopUnlessCond` (который захватывает это тело цикла). Обратите внимание, что тело еще не вычислено. Класс `LoopUnlessCond` имеет метод `unless`, который мы можем использовать как *инфиксный оператор*. Таким образом, мы получаем вполне естественный синтаксис для нашего нового цикла: `loop { < выражение > } unless ( < условие > )`.


Ниже приведен вывод выполнения `TargetTest2`:

i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1
Loading