Chiêu thức DefaultsTo trong Scala
Scala
50
White

Ngoc Dao viết ngày 20/03/2016

Giả sử ta muốn viết hàm convert nhận 2 tham số là chuỗi kí tự và kiểu dữ liệu, để biến chuỗi kí tự thành kiểu ta cần.

Làm bằng Java

Trong Java thì ta không thể thiết kế signature để hàm có dạng generic:

<T> convert(String value)

Vì không như C# giữ được thông tin về generic lúc runtime, Java có cơ chế type erasure để bỏ đi thông tin ấy, nhằm tương thích với phiên bản Java cũ. Ta phải thòng thêm kiểu ta cần như sau:

<T> convert(String value, Class<T> klass)

Làm bằng Scala

Type tag

Như bài Type erasure trong Java và manifest trong Scala giới thiệu, trong Scala ta có thể giữ lại thông tin về generic lúc runtime. Lúc compile time Scala compiler khi compile sẽ lưu lại thông tin về generic vào bytecode, lúc runtime thư viện chuẩn Scala sẽ móc thông tin đó ra rồi truyền lại cho hàm ta viết.

Bài viết trên dùng manifest đã bị deprecate từ Scala 2.10. Từ Scala 2.11 trở lên phải dùng type tag, như sau:

import scala.reflect.runtime.universe._

val TYPE_STRING  = typeOf[String]
val TYPE_CHAR    = typeOf[Char]
val TYPE_BOOLEAN = typeOf[Boolean]
val TYPE_BYTE    = typeOf[Byte]
val TYPE_SHORT   = typeOf[Short]
val TYPE_INT     = typeOf[Int]
val TYPE_LONG    = typeOf[Long]
val TYPE_FLOAT   = typeOf[Float]
val TYPE_DOUBLE  = typeOf[Double]

def convert[T: TypeTag](value: String): T = {
  val t = typeOf[T]
  val any: Any =
         if (t <:< TYPE_INT)    value.toInt
    else if (t <:< TYPE_DOUBLE) value.toDouble
    else if (t <:< TYPE_STRING) value
    else throw new Exception("Cannot covert " + value + " to " + t)
  any.asInstanceOf[T]
}

// Usage examples
val x = convert[Int]("123")
val y = convert[Double]("1.23")

Chiêu thức DefaultsTo

Ở trên ta trình bày cú pháp để lấy ra kiểu của tham số generic trong Scala. Nội dung bài viết này đến đây cũng chưa có gì mới so với bài Type erasure trong Java và manifest trong Scala. Tiếp theo mới là nội dung chính:

Đoạn mã sau:

convert("hi")

sẽ văng lỗi:

java.lang.NumberFormatException: For input string: "hi"
  at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
  at java.lang.Integer.parseInt(Integer.java:580)
  at java.lang.Integer.parseInt(Integer.java:615)
  at scala.collection.immutable.StringLike$class.toInt(StringLike.scala:272)
  at scala.collection.immutable.StringOps.toInt(StringOps.scala:30)

Nguyên nhân là khi không truyền T, mặc định nó sẽ là Nothing. Mà Nothing là kiểu đặc biệt, là con của tất cả các kiểu, nên sẽ cái if đầu tiên sẽ khớp, thành "hi".toInt.

Ta muốn nếu không truyền T, nó sẽ mặc định là String cơ!

Ta dùng chiêu thức này:

sealed class DefaultsTo[A, B]

trait LowPriorityDefaultsTo { 
  implicit def overrideDefault[A, B] = new DefaultsTo[A, B]
}

object DefaultsTo extends LowPriorityDefaultsTo {
  implicit def default[B] = new DefaultsTo[B, B]
}

Có cái DefaultsTo ở trên rồi, ta sửa convert thành như sau là xong:

def convert[T: TypeTag](value: String)(implicit e: T DefaultsTo String): T

Ghi chú về :paste mode

Đoạn mã DefaultsTo trên nếu viết thành tập tin .scala thì không sao. Nhưng nếu copy paste vào màn hình Scala console để chạy thử thì sẽ bị báo:

warning: previously defined class DefaultsTo is not a companion to object DefaultsTo.
Companions must be defined together; you may wish to use :paste mode for this.

Cần dùng ":paste mode" như trong trong thông báo trên, để class DefaultsTo và object DefaultsTo thành đôi bạn cùng tiến.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Ngoc Dao

102 bài viết.
284 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
62 8
Làm thế nào để nâng cấp trang web mà không làm gián đoạn dịch vụ? Đây là câu hỏi phỏng vấn các công ty lớn thường hỏi khi bạn xin vào vị trí làm lậ...
Ngoc Dao viết hơn 2 năm trước
62 8
White
40 1
Bài viết này giải thích sự khác khác nhau giữa hai ngành khoa học máy tính (computer science) và kĩ thuật phần mềm (software engineering), hi vọng ...
Ngoc Dao viết hơn 2 năm trước
40 1
White
34 1
Nếu là team leader, giám đốc công ty hay tướng chỉ huy quân đội, vấn đề cơ bản bạn gặp phải là “hướng mọi người đi theo con đường bạn chỉ ra”. Thử...
Ngoc Dao viết hơn 2 năm trước
34 1
Bài viết liên quan
White
10 0
Kí tự Regex cơ bản Về cơ bản thì các sử lý matching của scala.util.matching.Regex sẽ được "phó thác" (delegate) cho java Regex. Bạn có thể tạo một ...
huydx viết 3 năm trước
10 0
White
7 1
Trong scala kí tự _ được dùng với khá nhiều mục đích .. không liên quan đến nhau. Tạm note lại cái đã khi nào có time sẽ quay lại viết cẩn thận sa...
huydx viết 3 năm trước
7 1
White
0 0
(Bài viết hơi khó hiểu, dành cho bạn nào có hứng thú với type programming trong scala với các thư viện như shapeless chẳng hạn) Thông thường với m...
huydx viết hơn 2 năm trước
0 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
102 bài viết.
284 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!