Primary Constructor là gì? Secondary hay Auxiliary Constructor là gì ?
Scala có 2 kiểu constructors:
- Primary Constructor
- Auxiliary Constructor
Primary Constructor
Primary constructor là constructor được định nghĩa khi định nghĩa một class. Mỗi class phải có một Primary Constructor: Có tham số hoặc không
Ví dụ:
class Person
Class Person ở trên không có tham số trong Primary Constructor khi tạo một instances của class
class Person(firstName: String, lastName: String)
Class Person ở trên có 2 tham số cho Primary Constructor khi tạo một instances của class
Auxiliary Constructor
Auxiliary Constructor hay còn gọi là Secondary Constructor. Chúng ta có thể khai báo Secondary Constructor bằng cách sử dụng “def” hoặc “this” như bên dưới
class Person (firstName: String, middleName:String, lastName: String){ def this(firstName: String, lastName: String){ this(firstName, "", lastName) } }
Hãy giải thích cách sử dụng và quy tắc trong định nghĩa Auxiliary Constructor
Trong Scala, mục đích của Auxiliary Constructor là overload constructor. Giống như Java, Scala cung cấp các kiểu constructor khác nhau để có thể lựa chọn phù hợp với tham số đầu vào
Auxilary Constructor:
- Là một method, giống như method nó có thể sử dụng ‘def’ để định nghĩa
- Nên sử dụng tên ‘this’ cho tất cả Auxiliary Constructor
- Mỗi Auxiliary Constructor nên khác nhau về tham số: về số lượng hoặc kiểu
- Auxiliary Constructor không thể gọi một Super Class Constructor. Nó chỉ nên gọi thông qua Primary Constructor
- Tất cả Auxiliary Constructor gọi Primary Constructor trực tiếp hoắc gián tiếp qua Auxiliary Constructor khác
So sánh khác nhau giữa Array và ArrayBuffer trong Scala ?
Sự khác nhau giữa Array và ArrayBuffer là:
- Array cố định kích thước của array. Không thể thay đổi kích thước của array sau khi tạo
- ArrayBuffer thì kích thước của array có thể thay đổi được. Có thể tăng hoặc giảm kích thước của array
- Array giống Java’s primitive arrays
- ArrayBuffer giống Java’s ArrayList
case class là gì ? case object là gì ? Tác dụng của case class là gì?
Case class là class, được định nghĩa với từ khoá “case class”. Case object là một object được định nghĩa với từ khoá “case object”.
Chúng ta có thể tạo một case class mà không cần dùng “new” như class. Mặc định scala compiler sẽ dùng “val” cho tất cả tham số của constructor. Nó là lý do tại sao, nếu không sử dụng val hoăc var, case class constructor sẽ trở thành class members, điều này là không thể với class thông thường.
Tác dụng của case class:
- Mặc định Scala compiler thêm toString, hashCode và equals phương thức. Chúng ta không cần viết nhưng method này nữa
- Mặc định Scala compiler thêm companion object với apply và unapply phương thức. Lý do tại sao chúng ta không cần sử dụng “new” để tạo một instances cho case class
- Mặc định Scala compiler thêm copy phương thức
- Có thể sử dụng case class trong Pattern Matching
- Mặc định case class và case object là Serializable
So sánh khác nhau giữa case object và object
- object được tạo bằng cách sử dụng từ khoá “object”. Là một singleton object
- case object được tạo bằng cách sử dụng từ khoá “case object”. Mặc định nó cũng là một singleton object
- Mặc định case object có toString và hashCode phương thức. object bình thường thì không
- Mặc định Case object là serializable. Nhưng object bình thường thì không
Khi so sánh với class, những điểm mạnh của case class là gì ?
- Không phải viết boilerlate code vì nó thêm các method một cách tự động
- Mặc định hỗ trợ immutability bởi vì các tham số của nó là “val”
- sử dụng Pattern Matching dễ dàng
- Không cần dùng “new” để tạo instance cho case class
- Mặc định hỗ trợ serialization và deserializion
Cách sử dụng isInstanceOf và asInstanceOf phương thức trong scala ? Nó giống với định nghĩa nào trong Java không ?
“isInstanceOf” và “asInstanceOf” được định nghĩa trong bất kỳ một class nào. Nên không cần import những phương thức này trong bất kỳ class hoặc object
phương thức “isInstanceOf” được sử dụng để kiểm tra object có type hoặc không. Nếu đúng return true, hoặc không thì return false
scala > val str = “Hello” scala> str.isInstanceOf[String] Res0: Boolean = false
phương thức “asInstanceOf” được sử dụng để gán kiểu cho object. Nếu object và kiểu là giống nhau thì nó sẽ gán kiểu cho object, nếu sai thì throws java.lang.ClassCastException.
scala > val str = “Hello” scala> “Hello”.asInstanceOf[String] Str: String = Hello
Trong Java, “instanceof” giống với “isInstanceOf” của Scala. Trong Java, gán kiểu trực tiếp giống với phương thức “asInstanceOf”
AccountService service = (AccountService) context.getBean("accountService");
Làm thể nào để chứng minh mặc định case object là Serializable còn object bình thường thì không ?
Chúng ta có thể dùng phương thức isInstanceOf để kiểm tra:
scala> object MyNormalObject defined object MyNormalObject scala> MyNormalObject.isInstanceOf[Serializable] res0: Boolean = false scala> case object MyCaseObject defined object MyCaseObject scala> MyCaseObject.isInstanceOf[Serializable] res1: Boolean = true
Khác nhau giữa Array và List trong Scala ?
- Array thường là mutable, trong khi List luôn là immuable
- Sau khi tạo, chúng ta có thể thay đổi giá trị của array trong khi List không thể
- Array thì cố định kích thước của cấu trúc dữ liêu trong khi list có kích thước thay đổi theo cấu trúc dữ liệu. Kích thước của List tự động tăng hoặc giảm dựa theo cách chúng ta thay đổi nó
- Array là Invariants trong khi List là Convariant (có thể thay đổi kiểu)
Khác nhau giữa “val” và “lazy val” trong scala ? Eager Evalution là gì ? Lazy Evaluation là gì
Như chúng ta đã biết “val” là giá trị hoặc constant, được sử dụng như một immuable biến
Có 2 kiểu tính toán trong chương trình:
- Eager Evaluation
- Lazy Evaluation
Eager Evalution nghĩa là tính toán tại thời điểm biên dịch hay thời điểm chương trình chạy bất kể client có sử dụng chương trình hay không
Lazy Evalution nghĩa là tính toán tại thời điểm được yêu cầu, nghĩa là khi client truy cập chương trình thì nó sẽ được tính toán
Sự khác nhau giữa val và lazy val là val được sử dụng để định nghĩa các biến mà được tính toán ngay khi chạy chương trình còn lazy val được sử dụng để định nghĩa các biến nhưng được tính toán khi client truy cập vào
Ràng buộc giữa equals phương thức == trong scala ?
Trong Scala chúng ta không cần gọi phương thức equals để so sánh 2 instance hay object. Khi sử dụng == để so sánh, Scala sẽ gọi phương thức equals một cách tự động
Diamond lỗi là gì ?
Diamond lỗi xảy ra khi sử dụng nhiều kế thừa. Trong Scala nó xảy ra khi một class kế thừa nhiều trait mà có phương thức định nghĩa giống nhau. Không giống như Java 8, Scala giải quyết lỗi này một cách tự động theo một vài quy tắc được định nghĩa trong ngôn ngữ. Những quy tắc này gọi là “Class Linearization”
trait A{ def display(){ println("From A.display") } } trait B extends A{ override def display() { println("From B.display") } } trait C extends A{ override def display() { println("From C.display") } } class D extends B with C{ } object ScalaDiamonProblemTest extends App { val d = new D d display }
kết quả là “From C.display” của trait C. Scala compiler đọc “extend B with C” từ phải sang trái và gọi hàm “display” của trait cuối cùng là trait C
Tại sao Scala không có “static” ?
Như chúng ta biết thì scala không có “static”. Nó được quyết định bởi Scala Team
Lý do chính để đưa ra quyết định này là để tạo Scala như một Pure Object-Oriented ngôn ngữ. “Static” giúp chúng ta có thể truy cập class members mà không cần tạo object . Điều này là chống lại OOP cơ bản
Nếu một ngôn ngữ hỗ trợ “static” thì nghĩa rằng ngôn ngữ đấy không phải Pure Object-Oriented.
Cách sử dụng “object” trong Scala ? Làm thể nào để tạo một singleton object trong Scala ?
Trong Scala, từ khoá object được sử dụng theo các mục đích sau:
- Nó được dùng để tạo một singleton object trong Scala
object MySingletonObject
MySingletonObject trở thành singleton object một cách tự động
- Từ khoá object được sử dụng để định nghĩa trong Scala Application để có thể chạy chương trình Scala
object MyScalaExecutableProgram{ def main(args: Array[String]){ println("Hello World") } }
Khi định nghĩa hàm main trong object như ở trên, nó sẽ tự động được gọi khi chạy Scala program
- được sử dụng để định nghĩa một static members giống như biến static mà không cần dùng “static”
- được dùng để định nghĩa Factory phương thức
Làm thế nào để tạo phương thức Factory khi sử dụng object trong Scala ?
Trong Scala, chúng ta sử dụng object để định nghĩa phương thức Factory. Mục đích chính của các phương thức Factory trong scala là để tránh dùng “new”. Không cần sử dụng “new” chúng ta vẫn có thể tạo được các object.
Cách định nghĩa phương thức Factory:
Chúng ta có thể sử dụng apply và unapply để định nghĩa Factory method trong Scala. Nếu chúng ta có Primary Constructor và nhiều Auxiliary constructor thì chúng ta cần định nghĩa các apply method tương ứng như bên dưới:
class Person(val firstName: String, val middleName: String, val lastName: String){ def this(firstName: String, lastName: String){ this(firstName,"",lastName) } } object Person{ def apply(val firstName: String, val middleName: String, val lastName: String) = new Person(firstName,middleName,lastName) def apply(val firstName: String, val lastName: String) = new Person(firstName, lastName) }
Bây giờ thì chúng ta có thể tạo Person object mà không cần dùng “new”
val p1 = new Person("Scala","Java") or val p1 = Person("Scala","Java")
Apply và unapply trong Scala là gì ? So sánh sự khác nhau giữa 2 method ?
Trong Scala, apply và unapply là 2 phương thức rất quan trọng. Chúng rất hữu ích trong Play Framework trong việc mapping và unmapping dữ liệu giữa form và model
Phương thức apply:
Được dùng để tạo một object bằng dùng các thành phần của chính nó. Giả sử, chúng ta muốn tạo một Person Object, thì chúng ta có thể sử dụng firstName và lastName là 2 thành phần để tạo nên Person Object
class Person(val firstName: String, val lastName: String) object Person{ def apply(firstName: String, lastName: String) = new Person(firstName, lastName) }
Phương thức unapply:
Được dùng để phân tích một object ra các thành phần của nó. Nó theo quá trình ngược lại của apply. Giả sử chúng ta có một Person Object, thì chúng ta có thể phân tích object này ra 2 thành phần của nó là firstName và lastName như ví dụ bên dưới:
class Person(val firstName: String, val lastName: String) object Person{ def apply(firstName: String, lastName: String) = new Person(firstName, lastName) def unapply(p: Person): (String,String) = (p.firstName, p.lastName) }
Cách thức nào giúp chúng ta tạo một instance mà không cần sử dụng “new” trong Scala ? Khi nào thì chúng ta dùng phương thức này ? Làm thế nào để khai báo một private constructor trong scala ?
Khi chúng ta tạo một instance của Class mà không dùng “new”, Scala sẽ sử dụng một phương thức apply phù hợp mà có trong Companion Object. Phương thức apply phù hợp nghĩa là các tham số phải phù hợp với tham số của phương thức này
Khi nào thì chúng ta lựa chọn cách này ? Khi chúng ta cung cấp một private primary constrcutor và không muốn sử dụng từ khoá “new”, chúng ta có thể tạo một apply method với các tham số mong muốn và cho phép tạo class mà không cần dùng “new”
Làm cách nào để khai báo private primary constructor trong Scala ? Làm thế nào để gọi private primary constructor trong Scala ?
Trong Scala, chúng ta có thể dễ dàng khai báo một private primary constructor. Chỉ việc định nghĩa primary constructor và thêm private sau class name và trước tham số như ví dụ bên dưới:
class Person private (name: String) object Person{ def apply(name: String) = new Person(name) }
Như một private constructor, nó sẽ không thể gọi từ bên ngoài, chúng ta nên cung cấp một factory method ( nó chính là apply method) như ở trên và sử dụng constructor một cách gián tiếp
Một Companion Object có thể truy cập private members trong Companion Class trong Scala ?
Thông thường thì một private member chỉ có thể truy cập trong class đấy. Tuy nhiên Scala companion object và companion class đã cung cấp các đặc tính khác
Trong Scala, Companion object có thể truy cập private members của companion class và companion class có thể truy cập các private members của companion object
Lý do cho việc quyết định thiết kế tách class và object trong scala ? Làm thể nào để định nghĩa các instance member và static member trong scala ?
Trong Scala, chúng ta dùng Class để định nghĩa các instance members và object để định nghĩa các static members. Scala không có static nhưng chúng ta có thể định nghĩa chúng bằng cách sử dụng object
Lý do chính cho việc thiết kế này là nó tạo nên sự rõ ràng giữa instance và static members và liên kết lỏng lẻo giữa chúng. Lý do chính là việc tránh sử dụng static sẽ giúp Scala trở thành một Pure-OOP ngôn ngữ
Object trong Scala là gì ? Nó là Singleton object hay là instance của class?
Không giống như Java, Scala có 2 khái niệm về object. Bạn sẽ cảm thấy hơi khó hiểu, tôi sẽ giải thích nó một cách rõ ràng. Trong Java chúng ta chỉ có một khái niệm về object: Nó là instance của class.
- Giống như Java, trong Scala nó cũng là instance của một class
- Trong Scala, Object còn một khái niệm khác. Nó thường được dùng để định nghĩa nơi để chạy trong Scala application, companion object, Singleton object
Companion object là gi ? Companion Class là gì ? Companion object được sử dụng như thế nào ?
Hiểu một cách đơn giản thì nếu một class và một object dùng tên giống nhau và được đặt trong cùng một file thì class được hiểu như là “companion class” và object là “companion object”
Khi chúng ta tạo một class và một object với tên giống nhau trong cùng một file thì chúng là một companion class và companion object
class Employee{ } object Employee{ }
Trong Scala, mục đích chính trong việc dùng companion object là để định nghĩa một apply method và tránh phải dùng “new” khi tạo một instance của class
Làm thể nào để dùng interfaces trong Scala ?
Như chúng ta đã biết trong Java, chúng ta dùng interfaces để định nghĩa contract.
Tuy nhiên sẽ không có khái niệm interfaces trong scala. Thậm chí trong Scala không có từ khóa interfaces. Scala có khái niệm khác mạnh mẽ và linh hoạt hơn là trait. Trait là khái niệm tương đương interfaces trong Java
Range là gì ? Làm thế nào để tạo Range trong Scala ?
Range là một Lazy Collection trong Scala. Range là một class trong Scala package “scala.Range”. Nó được dùng để tạo ra các giá trị integer liên tiếp nhau:
scala> 1 to 10 res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> 1 until 10 res1: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
Kiểu Nothing là gì ?
Trong Scala, kiểu Nothing là không có giá trị hoặc 0. Nó là subtype của tất cả class hoăc reference class
Kiểu Unit là gì ?
Trong Scala, Unit hơi giống Java void. Nó được dùng với nghĩa “Không có giá trị”. Nó có một và chỉ một giá trị là “()”
Pure function là gì ?
Pure function là một function mà không có bất kỳ side-effects nào có thể quan sát thấy được. Nhĩa là hàm luôn trả về cùng một kết quả bất kể chúng ta gọi hàm đấy bao nhiêu lần với các tham số đầu vào là như nhau.
Pure function luôn trả về kết quả giống nhau với giá trị đầu vào là như nhau:
scala> 10 + 20 res0: Int = 30 scala> scala> 10 + 20 res0: Int = 30
Ví dụ ở trên “+” là một pure function trong Int class. Nó cho ra kết quả là 30 với giá trị đầu vào là 10 và 30, cho dù gọi bao nhiêu lần đi nữa
So sánh sự khác nhau giữa Function và Procdure ?
Cả 2 đều được sử dụng để thực hiện tính toán, tuy nhiên chúng có một chức năng chính trong thế giới Function Programming
function tính toán các đơn vị không có side-effect ( như giải thích ở trên), còn Procedure cũng tính toán các đơn vị nhưng với side effects
Khác nhau cơ bản giữa Scala Auxiliary Constructor và Java Constructor là gì ?
Scala Auxiliary Constructor gần giống với Java Constructor nhưng có một vài điểm khác nhau:
- Auxiliary Constructor được gọi bằng cách sử dụng từ khoá “this”
- Tất cả Auxiliary Constructor được định nghĩa với tên giống nhau, và đều dùng “this”. Trong Java, chúng ta sử dụng tên class để định nghĩa các constructors
- Mỗi auxiliary constructor phải bắt đầu bằng cách gọi đến một auxiliary được định nghĩa trước hoặc primary constructor
- Chúng ta sử dụng “def” để định nghĩa auxiliary constructor giống như định nghĩa method/ function. Trong Java, constructor được định nghĩa khác với định nghĩa method
“Yield” là gì trong for-comprehension constructor ?
Chúng ta có thể sử dụng “yield” trong for-comprehension constructor. “for/yield” được sử dụng để duyệt các phẩn tử của collection và tạo ra một collection mới với type tương tự. Nó sẽ không thay đổi cấu trúc của collection. Nó tạo ra một collection mới vớ type giống với collection gốc.
Ví dụ nếu ta dùng “for/yield” constructor để duyệt một List, thì nó sẽ tạo ra một List mới
scala> val list = List(1,2,3,4,5) list: List[Int] = List(1, 2, 3, 4, 5) scala> for(l <- list) yield l*2 res0: List[Int] = List(2, 4, 6, 8, 10)
Guard trong For-comprehension constructor là gì ?
Trong Scala, for-comprehension constructor sử dụng mệnh đề “if”. Nó được dùng như một điều kiện lọc các phần tử và sinh ra một collection mới. Mệnh đề if này được biết như là “Guard”
Nếu guard với điều kiện đúng thì thêm phần tử vào một collection mới. Ngoài ra, nó sẽ không thêm phần tử vào collection gốc
Ví dụ: for-comprehension guard tạo ra 7 số mới trong collection mới:
scala> val list = List(1,2,3,4,5,6,7,8,9,10) list: List[Int] = List(1, 2, 3, 4, 5 , 6 , 7 , 8 , 9 , 10) scala> for(l <- list if l % 2 =0 ) yield l res0: List[Int] = List(2, 4, 6, 8, 10)
Scala giải quyết vấn đề Diamond kế thừa tự động như thế nào và dễ hơn Java 8 không ?
Nếu chúng ta sử dụng Java 8 Interface với phương thức mặc định, chúng ta sẽ gặp vấn đề về Diamond kế thừa. Lập trình viên sẽ tự giải quyết nó trong Java 8, vì nó sẽ không cung cấp mặc định hay giải quyết tự động cho vấn đề này
Trong Scala, chúng ta sẽ gặp vấn đề tương tự với Trait. Nhưng Scala rất thông minh và giải quyết vấn đề Diamond kế thừa tự động bằng cách sử dụng khái niệm “class linearization”