Câu hỏi phỏng vấn Scala phần 1

scala

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”

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.