Tóm Tắt
Kotlin Property và field
1. Khai báo
property
¶
property
Các class
trong Kotlin có thể có các property
. Chúng có thể khai báo là các biến – sử dụng từ khóa var
hoặc các constant – sử dụng từ khóa val
.
Java
class
Address
{
String
name
;
String
street
;
String
city
;
String
state
;
String
zip
;
}
Kotlin
class
Address
{
var
name
:
String
=
...
var
street
:
String
=
...
var
city
:
String
=
...
var
state
:
String
?
=
...
var
zip
:
String
=
...
}
Để sử dụng các property
này, chúng ta chỉ cần access đến chúng bằng tên, hoặc bằng các hàm getter/setter
như trong Java
Java
public
Address
copyAddress
(
Address
address
)
{Address
result
=
new
Address
()
;
result
.setName
(
address
.getName
())
;
result
.setStreet
(
address
.getStreet
())
;
//
....return
result
;
}
Kotlin
fun
copyAddress
(
address
:
Address
):
Address
{
val
result
=
Address
()
// không còn từ khóa 'new' trong Kotlin
result
.
name
=
address
.
name
//các hàm 'getter/setter' sẽ được gọi, dù nhìn trông như bạn đang truy cập trực tiếp vào 'property'
result
.
street
=
address
.
street
// ...
return
result
}
2. Các hàm
getter
/
setter
¶
getter
/
setter
Cấu trúc đầy đủ của khai báo property
trong Kotlin là:
var
<
propertyName
>[:
<
PropertyType
>]
[=
<
property_initializer
>]
[<
getter
>]
[<
setter
>]
Trong đó, giá trị khởi tạo, và các hàm getter/setter
là không bắt buộc, kiểu dữ liệu cũng là không bắt buộc nếu nó có thể được suy ra từ việc khởi tạo (hoặc từ kiểu của mà hàm getter
trả về, sẽ được nói phía sau). Tuy nhiên, để code được trong sáng, lời khuyên là nên thêm kiểu của thuộc tính khi khai báo.
var
allByDefault
:
Int
?
// compiler báo lỗi vì việc khởi tạo được yêu cầu, các hàm 'getter/setter' mặc định được chỉ định
var
initialized
=
1
// kiểu 'Int', hàm 'getter/setter' mặc định
Với các constant, việc khai báo sử dụng từ khóa val
và không được định nghĩa hàm setter
:
val
simple
:
Int
?
// compiler báo lỗi: yêu cầu việc khởi tạo, 'getter' mặc định
val
inferredType
=
1
// kiểu 'Int', hàm 'getter' mặc định
Với các hàm getter/setter
, nếu không được định nghĩa, các hàm getter/setter
mặc định sẽ được sử dụng. Chúng ta có thể định nghĩa các hàm này ngay sau việc khai báo các property
. Mặc định, tên của param
của hàm setter
là value
. Tuy nhiên, bạn có thể chọn một cái tên khác. Nếu thích!
var
isEmpty
:
Boolean
get
()
{
//
return
field
// hàm 'getter' mặc định
}
//
set
(
value
)
{
field
=
value
}
var
isEmpty
:
Boolean
get
()
=
this
.
size
==
0
// hàm 'getter' tự định nghĩa
set
(
value
){
//
(
"Setter: $value"
)
//hàm 'setter' tự định nghĩa
field
=
value
//
}
Từ Kotlin 1.1, bạn có thể bỏ qua kiểu dữ liệu của property
nếu nó có thể được suy ra từ kiểu trả về của hàm getter
:
val
isEmpty
get
()
=
this
.
size
==
0
// isEmpty sẽ có kiểu là Boolean
Ngoài ra, ta cũng có thể xác định visibility modifier của hàm setter
. Lưu ý: modifier của setter
phải có phạm vi không được lớn hơn phạm vi modifier của property
. Với getter
, chúng ta không thể thay đổi modifier của hàm getter
bởi modifier của getter
phải giống với modifier của property
. Bạn cũng có thể chỉ xác định lại modifier của hàm setter
mà không cần implement hàm đó:
var
setterVisibility
:
String
=
"abc"
private
set
//
hàm
'setter'
có
modifier
là
'private'
và
việc
implement
là
mặc
đị
nh
3. Backing field¶
Trong một VD ở phía trên trên, ta có thể thấy một biến xuất hiện trong các hàm getter/setter
tự định nghĩa, đó là field
. Lưu ý, field
chỉ có thể sử dụng bên trong các hàm getter/setter
và field
sẽ được tự động gen cho property
nếu một trong các hàm getter/setter
tham chiếu đến nó. Nếu không, property
sẽ không có field
. Nhưng vì sao phải dùng field
thay vì dùng property
một cách trực tiếp như thế này:
var
isEmpty
:
Boolean
get
()
=
{
return
isEmpty
}
set
(
value
){
isEmpty
=
value
}
Như trong một vd ở trên đã đề cập, khi bạn access đến một property
:
val
result
=
Address
()
result
.
name
=
address
.
name
Khi này, thực chất, hàm setter
và getter
của property name
sẽ được gọi chứ không phải bạn đang access trực tiếp đến name
. Bởi vậy, trong các hàm getter/setter
tự định nghĩa, nếu sử dụng trực tiếp các property
(vd hàm getter
), Kotlin sẽ gọi lại chính hàm getter
đó, từ đó gây ra tràn bộ nhớ Stack – StackOverflowError
.
4. Backing property¶
Nếu bạn không quen (hoặc không thích) cách dùng field
ở trên, bạn có thể sử dụng backing property. Việc này tương tự như trong Java, và các hàm getter/ setter
sẽ được tối ưu để việc tràn bộ nhớ không xảy ra. Tuy nhiên, việc viết code sẽ vất vả hơn, tất nhiên rồi:
private
var
_table
:
Map
<
String
,
Int
>?
=
null
public
var
table
:
Map
<
String
,
Int
>
get
()
{
if
(
_table
==
null
)
{
_table
=
HashMap
()
// Type parameters are inferred
}
return
_table
?:
throw
AssertionError
(
"Set to null by another thread"
)
}
set
(
value
)
{
_table
=
value
}
Trong VD trên, property
mà chúng ta sử dụng để lưu dữ liệu là _table
còn table
chỉ là cách thức để chúng ta truy cập đến _table
.
Các thuộc tính mà giá trị của chúng được biến đến lúc compile có thể được đánh dấu là compile time constant, sử dụng từ khóa const
. Những property
để đạt được cần thỏa mãn nhưng yêu cầu sau:
- Là top-level
property
object
- Được khởi tạo với kiểu
String
- Không được có hàm
getter
const
val
SUBSYSTEM_DEPRECATED
:
String
=
"This subsystem is deprecated"
@Deprecated
(
SUBSYSTEM_DEPRECATED
)
fun
foo
()
{
...
}
6. Late-initialized property (Khởi tạo chậm các thuộc tính)¶
Bình thường, khi các property
được khai báo trong class mà thuộc kiểu non-null phải được khởi tạo: trực tiếp hoặc bằng constructor. Tuy nhiên, việc này không được tiện cho lắm. VD: property
có thể được khởi tạo thông qua Dependency injection hoặc được khởi tạo bên trong method setup của một unit tets hoặc được gán trong một method khác bên trong class. Bởi vậy, Kotlin cung cấp cơ chế cho phép delay việc khởi tạo: từ khóa lateinit
class
Teacher
(
var
name
:
String
,
var
age
:
Int
)
{
lateinit
var
className
:
String
}
Yêu cầu để sử dụng được từ khóa lateinit
là:
- Phải sử dụng với
var
property
property
getter
/
setter
- Kiểu của các
property
- Nếu truy cập các
property
kotlin
.
UninitializedPropertyAccessException
7. Overriding property (Ghi đè các
property
)¶
property
Trong Kotlin, chúng ta có thể ghi đè các property
bằng cách sử dụng từ khóa override
tương tự như override các method.
open
class
Foo
{
open
val
x
:
Int
get
{
...
}
}
class
Bar1
:
Foo
()
{
override
val
x
:
Int
=
...
}
Đặc biệt, chúng ta có thể override lại một val
property bằng một var
property, nhưng không thể làm điều ngược lại. Điều này được phép bởi vì một val
property đã khai báo hàm getter
, và khi override lại nó là var
, chúng ta cần viết thêm hàm setter
trong class con.
Từ khóa override
cũng có thể sử dụng ngay trong primary constructor:
interface
Foo
{
val
count
:
Int
}
class
Bar1
(
override
val
count
:
Int
)
:
Foo
class
Bar2
:
Foo
{
override
var
count
:
Int
=
0
}