Skip to content

Commit 1b6d17f

Browse files
artemkorsakovjulienrf
authored andcommitted
Update lower-type-bounds.md and upper-type-bounds.md in russian
1 parent ea6e79e commit 1b6d17f

File tree

2 files changed

+124
-28
lines changed

2 files changed

+124
-28
lines changed

_ru/tour/lower-type-bounds.md

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,57 +9,107 @@ previous-page: upper-type-bounds
99
prerequisite-knowledge: upper-type-bounds, generics, variance
1010
---
1111

12-
В то время как [верхнее ограничение типа](upper-type-bounds.html) ограничивает тип до подтипа стороннего типа, *нижнее ограничение типа* объявляют тип супертипом стороннего типа. Термин `B >: A` выражает, то что параметр типа `B` или абстрактный тип `B` относится к супертипу типа `A`. В большинстве случаев `A` будет задавать тип класса, а `B` задавать тип метода.
12+
В то время как [верхнее ограничение типа](upper-type-bounds.html) ограничивает тип до подтипа стороннего типа, _нижнее ограничение типа_ объявляют тип супертипом стороннего типа. Термин `B >: A` выражает, то что параметр типа `B` или абстрактный тип `B` относится к супертипу типа `A`. В большинстве случаев `A` будет задавать тип класса, а `B` задавать тип метода.
1313

1414
Вот пример, где это полезно:
1515

16+
{% tabs upper-type-bounds_1 class=tabs-scala-version %}
17+
{% tab 'Scala 2' for=upper-type-bounds_1 %}
18+
1619
```scala mdoc:fail
17-
trait Node[+B] {
18-
def prepend(elem: B): Node[B]
20+
trait List[+A] {
21+
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
1922
}
2023

21-
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
22-
def prepend(elem: B): ListNode[B] = ListNode(elem, this)
23-
def head: B = h
24-
def tail: Node[B] = t
25-
}
24+
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
2625

27-
case class Nil[+B]() extends Node[B] {
28-
def prepend(elem: B): ListNode[B] = ListNode(elem, this)
29-
}
26+
object Nil extends List[Nothing]
27+
```
28+
29+
{% endtab %}
30+
{% tab 'Scala 3' for=upper-type-bounds_1 %}
31+
32+
```scala
33+
trait List[+A]:
34+
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
35+
36+
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
37+
38+
object Nil extends List[Nothing]
3039
```
3140

32-
В данной программе реализован связанный список. `Nil` представляет пустой список. Класс `ListNode` - это узел, который содержит элемент типа `B` (`head`) и ссылку на остальную часть списка (`tail`). Класс `Node` и его подтипы ковариантны, потому что у нас указанно `+B`.
41+
{% endtab %}
42+
{% endtabs %}
43+
44+
В данной программе реализован связанный список. `Nil` представляет пустой список. Класс `NonEmptyList` - это узел, который содержит элемент типа `A` (`head`) и ссылку на остальную часть списка (`tail`). `trait List` и его подтипы ковариантны, потому что у нас указанно `+A`.
3345

34-
Однако эта программа _не компилируется_, потому что параметр `elem` в `prepend` имеет тип `B`, который мы объявили *ко*вариантным. Так это не работает, потому что функции *контр*вариантны в типах своих параметров и *ко*вариантны в типах своих результатов.
46+
Однако эта программа _не компилируется_, потому что параметр `elem` в `prepend` имеет тип `A`, который мы объявили *ко*вариантным. Так это не работает, потому что функции *контр*вариантны в типах своих параметров и *ко*вариантны в типах своих результатов.
3547

36-
Чтобы исправить это, необходимо перевернуть вариантность типа параметра `elem` в `prepend`. Для этого мы вводим новый тип для параметра `U`, у которого тип `B` указан в качестве нижней границы типа.
48+
Чтобы исправить это, необходимо перевернуть вариантность типа параметра `elem` в `prepend`. Для этого мы вводим новый тип для параметра `B`, у которого тип `A` указан в качестве нижней границы типа.
49+
50+
{% tabs upper-type-bounds_2 class=tabs-scala-version %}
51+
{% tab 'Scala 2' for=upper-type-bounds_2 %}
3752

3853
```scala mdoc
39-
trait Node[+B] {
40-
def prepend[U >: B](elem: U): Node[U]
54+
trait List[+A] {
55+
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
4156
}
4257

43-
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
44-
def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this)
45-
def head: B = h
46-
def tail: Node[B] = t
47-
}
58+
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
4859

49-
case class Nil[+B]() extends Node[B] {
50-
def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this)
51-
}
60+
object Nil extends List[Nothing]
61+
```
62+
63+
{% endtab %}
64+
{% tab 'Scala 3' for=upper-type-bounds_2 %}
65+
66+
```scala
67+
trait List[+A]:
68+
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
69+
70+
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
71+
72+
object Nil extends List[Nothing]
5273
```
5374

75+
{% endtab %}
76+
{% endtabs %}
77+
5478
Теперь мы можем сделать следующее:
79+
80+
{% tabs upper-type-bounds_3 %}
81+
{% tab 'Scala 2 и 3' for=upper-type-bounds_3 %}
82+
5583
```scala mdoc
5684
trait Bird
5785
case class AfricanSwallow() extends Bird
5886
case class EuropeanSwallow() extends Bird
5987

88+
val africanSwallows: List[AfricanSwallow] = Nil.prepend(AfricanSwallow())
89+
val swallowsFromAntarctica: List[Bird] = Nil
90+
val someBird: Bird = EuropeanSwallow()
91+
92+
// присвоить птицам (birds) африканских ласточек (swallows)
93+
val birds: List[Bird] = africanSwallows
6094

61-
val africanSwallowList= ListNode[AfricanSwallow](AfricanSwallow(), Nil())
62-
val birdList: Node[Bird] = africanSwallowList
63-
birdList.prepend(EuropeanSwallow())
95+
// добавляем новую птицу к африканским ласточкам, `B` - это `Bird`
96+
val someBirds = africanSwallows.prepend(someBird)
97+
98+
// добавляем европейскую ласточку к птицам
99+
val moreBirds = birds.prepend(EuropeanSwallow())
100+
101+
// соединяем вместе различных ласточек, `B` - это `Bird`, потому что это общий супертип для обоих типов ласточек
102+
val allBirds = africanSwallows.prepend(EuropeanSwallow())
103+
104+
// но тут ошибка! добавление списка птиц слишком расширяет тип аргументов. -Xlint предупредит!
105+
val error = moreBirds.prepend(swallowsFromAntarctica) // List[Object]
64106
```
65-
`Node[Bird]` может быть присвоен `africanSwallowList` , но затем может добавлять и `EuropeanSwallow`.
107+
108+
{% endtab %}
109+
{% endtabs %}
110+
111+
Параметр ковариантного типа позволяет `birds` получать значение `africanSwallows`.
112+
113+
Тип, связанный с параметром типа `prepend`, позволяет добавлять различные разновидности ласточек и получать более абстрактный тип: вместо `List[AfricanSwallow]`, мы получаем `List[Bird]`.
114+
115+
Используйте `-Xlint`, чтобы предупредить, если аргумент предполагаемого типа слишком абстрактен.

_ru/tour/upper-type-bounds.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ previous-page: variances
1212
В Scala [параметры типа](generic-classes.html) и [члены абстрактного типа](abstract-type-members.html) могут быть ограничены определенными диапазонами. Такие диапазоны ограничивают конкретные значение типа и, возможно, предоставляют больше информации о членах таких типов. _Верхнее ограничение типа_ `T <: A` указывает на то что тип `T` относится к подтипу типа `A`.
1313
Приведем пример, демонстрирующий верхнее ограничение для типа класса `PetContainer`:
1414

15+
{% tabs upper-type-bounds class=tabs-scala-version %}
16+
{% tab 'Scala 2' for=upper-type-bounds %}
17+
1518
```scala mdoc
1619
abstract class Animal {
1720
def name: String
@@ -39,10 +42,53 @@ val dogContainer = new PetContainer[Dog](new Dog)
3942
val catContainer = new PetContainer[Cat](new Cat)
4043
```
4144

45+
{% endtab %}
46+
{% tab 'Scala 3' for=upper-type-bounds %}
47+
48+
```scala
49+
abstract class Animal:
50+
def name: String
51+
52+
abstract class Pet extends Animal
53+
54+
class Cat extends Pet:
55+
override def name: String = "Cat"
56+
57+
class Dog extends Pet:
58+
override def name: String = "Dog"
59+
60+
class Lion extends Animal:
61+
override def name: String = "Lion"
62+
63+
class PetContainer[P <: Pet](p: P):
64+
def pet: P = p
65+
66+
val dogContainer = PetContainer[Dog](Dog())
67+
val catContainer = PetContainer[Cat](Cat())
68+
```
69+
70+
{% endtab %}
71+
{% endtabs %}
72+
73+
{% tabs upper-type-bounds_error class=tabs-scala-version %}
74+
{% tab 'Scala 2' for=upper-type-bounds_error %}
75+
4276
```scala mdoc:fail
4377
// это не скомпилируется
4478
val lionContainer = new PetContainer[Lion](new Lion)
4579
```
80+
81+
{% endtab %}
82+
{% tab 'Scala 3' for=upper-type-bounds_error %}
83+
84+
```scala
85+
// это не скомпилируется
86+
val lionContainer = PetContainer[Lion](Lion())
87+
```
88+
89+
{% endtab %}
90+
{% endtabs %}
91+
4692
Класс `PetContainer` принимает тип `P`, который должен быть подтипом `Pet`. `Dog` и `Cat` - это подтипы `Pet`, поэтому мы можем создать новые `PetContainer[Dog]` и `PetContainer[Cat]`. Однако, если мы попытаемся создать `PetContainer[Lion]`, то получим следующую ошибку:
4793

4894
`type arguments [Lion] do not conform to class PetContainer's type parameter bounds [P <: Pet]`

0 commit comments

Comments
 (0)