Tại sao mọi class con cần gọi đến default constructor của class cha?

Mình muốn hỏi chính xác thì default constructor làm gì?
Vì trước đó mình có một vài hiểu lầm về constructor nên ở dưới mình trình bày ý hiểu của mình về constructor nói chung và điều mình confuse.


Định nghĩa:

A constructor is used in the creation of an object that is an instance of a class
(Một constructor được sử dụng trong quá trình tạo object, trước mình nhầm nó để tạo object).

Theo lý thuyết, thì có 2 thành phần được dùng để tạo object là toán tử new & constructor.
Quá trình tạo object:

  1. toán tử new: cấp phát vùng nhớ cho instance, sau bước này thì đối tượng this tồn tại
  2. Khởi tạo giá trị cho instance variable
  3. Gọi hàm constructor
VD: class A{
	int x = this.y;
	int y = 10;
}
A a1 = new A();
/*
B1: toán tử new: init vùng nhớ cho a1 
B2: gán giá trị: this.y lúc này có giá trị 0 nên x = 0, y = 10
y là members variables nên có thể access ở bất cứ đâu trong class, 
tức dùng this chấm ra được. Cơ mà this ở đây nằm ngoài constructors, 
mà quá trình khởi tạo giá trị cho instance value chạy trước constructor, 
tức this tồn tại trước khi call constructor, vậy nên confirm đc constructor ko 
phải để tạo đối tượng. Nếu có bắt buộc phải có constructor mới tạo được 
instance thì là vì một lý do khác)
B3: gọi constructor 
*/

Vậy constructor là một hàm được gọi sau cùng trong quá trình tạo đối tượng, mục đích để khởi tạo giá trị cho thuộc tính mà người dùng mong muốn tác động vào. Vậy thực sự thì, không cần có default constructor thì object vẫn tạo được, nhưng tại sao java lại cung cấp default constructor nếu ko tự định nghĩa, mà nếu tự định nghĩa thì default constructor biến mất? (1)

Tiếp, khai báo một class cha:

public class Parent{
     public Parent(Strings){}
}
//vì đã khai báo constructor tự định nghĩa nên default constructor biến mất.

Khai báo class con:

public class Child extends Parent{
}
/*Lỗi biên dịch vì có luật: một constructor nếu không bắt đầu bằng super 
hoặc this và ko phải class Object, thì constructor đó sẽ ngầm gọi super(), 
tức gọi đến constructor rỗng của class cha trực tiếp của nó. Lỗi biên dịch 
xảy ra khi một class có default constructor được tạo ngầm nhưng ở super
class ko có constructor rỗng nào có thể truy cập được.
*/

Vậy khi tạo 1 class con, luôn phải gọi đến constructor rỗng của cha, với mọi trường hợp cuối cùng đều call tới Object(), mà call constructor rỗng thì mình ko thấy có tác dụng gì cả, tại sao phải bắt buộc?
Thanks!!

Default constructor, thì ý nghĩa đúng như tên, dùng để tạo 1 instance có giá trị “mặc định”.
Có thể hiểu public int y = 10; là đã gán mặc định giá trị 10 cho thuộc tính y. Nên default constructor sẽ không cần làm gì thêm nữa => tác dụng của default constructor chỉ còn là để phân biệt với các constructor khác.

Đến tại sao có định nghĩa constructor khác thì default constructor lại mất: instance luôn cần có constructor. Khi không cần gì đặc biệt thì có thể dùng default constructor. Bây giờ giả sử cần làm gì đó đặc biệt như chấp nhận giá trị là đường dẫn file rồi khởi tạo thuộc tính của instance từ file này => cần constructor không phải default => giờ default constructor nếu có lại không làm đúng nữa => không tạo default constructor.

Vấn đề cuối cùng, tại sao class con cần phải gọi constructor của class cha. Mỗi class phải có nhiệm vụ khởi tạo thuộc tính của chính nó và chỉ các thuộc tính này. Nên class con không thể khởi tạo cho class cha, mà phải thông qua constructor của class cha. Nếu không có gọi tới super 1 cách tường minh thì mặc định sẽ gọi tới default constructor. Nhưng bởi vì có public Parent(Strings){} nên default constructor biến mất => lỗi.
Cách fix: gọi super tường minh hoặc thêm default constructo cho class cha:

public class Child extends Parent{
   public Child(){
      super("somestring");
   }
}
12 Likes

Tạo 1 instance có giá trị mặc định, nghĩa là ko cần thêm bất cứ thông tin nào từ user, chính xác nó là use no-arg constructor. Còn default constructor là case khi sử dụng no-arg constructor mà body empty, thì ko cần viết ra. Ý là giống ko có constructor, nhưng muốn có khái niệm cho dễ phân biệt.
Còn 1 điểm là java tự tạo default constructor mà trong thân hàm gọi mỗi super(), khi tự định nghĩa 1 constructor riêng thì constructor tự định nghĩa đấy cũng ngầm gọi super(), nên nghĩ ko cần đến default constructor nữa chứ ko phải vì lỗi. VD mình có constructor nhận đường dẫn file rồi tạo thuộc tính của instance từ file này, default constructor nếu có tồn tại cũng ko vấn đề gì đâu (nó tồn tại giống 1 no-arg constructor thôi).

Theo mình hiểu thì class con đâu cần khởi tạo các thuộc tính cho class cha. Khi tạo 1 instance cho class con thì class con đó kế thừa members của class cha, mà kế thừa thì là thành của chính nó rồi, nên init thì nó init mỗi thuộc tính của nó. Gọi super(“xyz”) là do constructor ko kế thừa được nên call để sử dụng lại.
Hay do mình hiểu sai.

2 Likes

Không phải gọi đên default của class cha.
Mà bơi vì Child không có construcctor, nên nó sẽ tạo ngầm default

Child()
{
    super();
}

em mở file .class lên sẽ thấy nó tạo ra contructor như thế.
Mà supper của Child là Parrent, mà parrent hiện tại không có contrunsctor Parrent(), nên lỗi là đúng rồi.

7 Likes

@vivungve giải thích giúp em chỗ này với! 🙆 Tại sao x không bằng 10? Thì do gán 10 cho y sau khi gán y cho x nhưng em vẫn còn mơ hồ, chưa hiểu đoạn này. Với lại cho em hỏi khi gán giá trị cho x diễn ra lúc compile hay lúc runtime, dùng this chắc chắn :100:% là lúc runtime đúng không nhỉ?:thinking:

tại zì x và y là kiểu số nguyên, lúc cấp phát vùng nhớ kiểu int có giá trị default = 0
zì x không phải kiểu tham chiếu, nên nó nhận giá trị của y tại thời điểm y được gán: ở dòng gán x = this.y thì y có giá trị default = 0 nên x nhận giá trị y = 0

3 Likes

Vậy là lúc x có giá trị bằng this.y diễn ra lúc runtime đúng không? Lúc compile thì không có đối tượng this nào được tạo ra hết? 🙆 :bowing_man:

Đúng rồi cậu @@
Compile time chẳng có đối tượng nào cả mà :smile:

3 Likes

Anh ơi sao em không thấy nó tạo ngầm có method super() vậy anh?

File Parent.java

public class Parent {
   }

File Child.java

public class Child extends Parent {
    public static void main(String[] args) {
        new Child();
    }
}

File Parent.class (đã được decomplie)

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

public class Parent {
    public Parent() {
    }
}

File Child.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

public class Child extends Parent {
    public Child() {
    }

    public static void main(String[] args) {
        new Child();
    }
}


83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?