1. Tất cả các lớp kế thừaObject

Tất cả các lớp trong Java đều ngầm kế thừa Objectlớp.

Chúng ta sẽ phân tích kế thừa là gì và nó hoạt động như thế nào trong Java trong nhiệm vụ Java Core. Bây giờ, chúng ta sẽ xem xét một thực tế đơn giản sau đây:

Một đối tượng của bất kỳ lớp nào có thể được gán cho một Objectbiến. Ví dụ:

Mã số Ghi chú
Object o = new Scanner(System.in);
Biến olưu trữ một tham chiếu đến một Scannerđối tượng
Object o = new String();
Biến olưu trữ một tham chiếu đến một Stringđối tượng
Object o = new Integer(15);
Biến olưu trữ một tham chiếu đến một Integerđối tượng
Object o = "Hello";
Biến olưu trữ một tham chiếu đến một Stringđối tượng

Đây là nơi tin tốt kết thúc. Trình biên dịch không theo dõi loại đối tượng ban đầu được lưu trong một Objectbiến, vì vậy bạn sẽ không thể gọi các phương thức trên đối tượng đã lưu ngoài các phương thức của Objectlớp.

Nếu bạn cần gọi các phương thức được liên kết với kiểu ban đầu của đối tượng, thì trước tiên bạn cần lưu một tham chiếu đến nó trong một biến có kiểu chính xác, sau đó gọi các phương thức trên biến đó:

Mã số Ghi chú
Object o = new Scanner(System.in);
int x = o.nextInt();
Chương trình sẽ không biên dịch. Lớp Objectkhông có nextInt()phương thức.
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
Điều này sẽ làm việc.

Ở đây chúng tôi lưu một tham chiếu đến một Scannerđối tượng trong một Scannerbiến bằng cách sử dụng toán tử định kiểu .

Bạn không thể chỉ đi và gán một Objectbiến cho một biến Máy quét, ngay cả khi Objectbiến đó lưu trữ một Scannerđối tượng tham chiếu. Nhưng bạn có thể làm điều này nếu bạn sử dụng toán tử typecast mà bạn đã biết. Đây là diện mạo chung của nó:

Type name1 = (Type) name2;

Đâu name1là tên của một Typebiến, và name2là tên của một Objectbiến lưu trữ một tham chiếu đến một Typeđối tượng.

đánh máy

Nếu kiểu của biến và kiểu của đối tượng không khớp, thì a ClassCastExceptionsẽ bị ném ra. Ví dụ:

Mã số Ghi chú
Object o = new Integer(5);
String s = (String) o;
Sẽ xảy ra lỗi khi chạy:
a ClassCastExceptionsẽ được ném vào đây

Có một cách để tránh lỗi này trong Java: chúng tôi thực hiện việc này bằng cách kiểm tra loại đối tượng được lưu trữ trong một biến :

name instanceof Type

Toán instanceoftử kiểm tra xem namebiến có phải là một Typeđối tượng hay không.

Ví dụ, hãy tìm một chuỗi trong một mảng các đối tượng khác nhau:

Mã số Ghi chú
Object[] objects = {10, "Hello", 3.14};

for (int i = 0; i < objects.length; i++)
{
   if (objects[i] instanceof String)
   {
      String s = (String) objects[i];
      System.out.println(s);
   }
}
Autoboxing sẽ chuyển đổi các giá trị này thành Integer, StringDouble, tương ứng.

Lặp lại mảng các đối tượng

Nếu đối tượng là một String

Lưu nó vào một Stringbiến
Hiển thị biến trên màn hình.


2. Tại sao thuốc generic xuất hiện — bộ sưu tập

Hãy trở lại bộ sưu tập.

Ngay khi các nhà phát triển Java tạo ra ArrayListlớp này, họ muốn làm cho nó trở nên phổ biến, để nó có thể lưu trữ bất kỳ loại đối tượng nào. Vì vậy, họ đã sử dụng một mảng Objects để lưu trữ các phần tử.

Điểm mạnh của phương pháp này là bạn có thể thêm một đối tượng thuộc bất kỳ loại nào vào bộ sưu tập.

Tất nhiên, có một số điểm yếu.

Nhược điểm 1.

Luôn cần phải viết một toán tử chuyển đổi kiểu khi truy xuất các phần tử từ một tập hợp:

Mã số Ghi chú
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 10);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Tạo một bộ sưu tập để lưu trữ các tham chiếu đến Objectcác đối tượng

Điền vào bộ sưu tập các số 10, 20, ... 100;



Tính tổng các phần tử của bộ sưu tập


Việc đánh máy là cần thiết

Nhược điểm 2.

Không có gì đảm bảo rằng một bộ sưu tập chứa một loại phần tử cụ thể

Mã số Ghi chú
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 2.5);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Tạo một bộ sưu tập để lưu trữ các tham chiếu đến Objectcác đối tượng

Chúng tôi điền vào bộ sưu tập các số được biểu diễn dưới dạng Doublecác đối tượng:
0.0, 2.5, 5.0, ...


Tính tổng các phần tử của bộ sưu tập


Sẽ xảy ra lỗi: a Doublecould be cast to anInteger

Dữ liệu có thể được đưa vào bộ sưu tập ở bất cứ đâu:

  • trong một phương pháp khác
  • trong một chương trình khác
  • từ một tập tin
  • qua mạng

Nhược điểm 3.

Dữ liệu trong bộ sưu tập có thể bị thay đổi một cách tình cờ.

Bạn có thể chuyển một bộ sưu tập chứa đầy dữ liệu của mình tới một phương thức nào đó. Phương thức đó, được viết bởi một lập trình viên khác, sẽ thêm dữ liệu của nó vào bộ sưu tập của bạn.

Tên của bộ sưu tập không chỉ rõ loại dữ liệu nào có thể được lưu trữ trong đó. Và ngay cả khi bạn đặt cho biến của mình một cái tên rõ ràng, thì một tham chiếu đến nó có thể được chuyển đến hàng chục phương thức và những phương thức đó chắc chắn sẽ không biết gì về tên ban đầu của biến.


3. Thuốc gốc

Generics trong Java

Trong Java, tất cả những vấn đề này đều được loại bỏ bởi thứ hay ho này gọi là generics.

Trong Java, khái quát có nghĩa là khả năng thêm các tham số kiểu vào các kiểu. Kết quả là một loại hỗn hợp phức tạp. Quan điểm chung của một loại hỗn hợp như vậy là:

ClassName<TypeParameter>

Đây là một lớp chung. Và nó có thể được sử dụng ở bất cứ nơi nào bạn thường sử dụng các lớp học.

Mã số Sự miêu tả
ArrayList<Integer> list;
Tạo biến
list = new ArrayList<Integer> ();
Tạo đối tượng
ArrayList<Integer>[] array;
Tạo mảng

Chỉ Integercác biến có thể được lưu trữ trong một bộ sưu tập như vậy:

Mã số Sự miêu tả
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Hello");
ArrayListbộ sưu tập với Integercác yếu tố
Điều này được cho phép
Và điều này cũng sẽ hoạt động
hộp tự động

Nhưng điều này không được phép: lỗi biên dịch

Bạn sẽ học cách tạo các lớp của riêng mình với các tham số kiểu trong nhiệm vụ Bộ sưu tập Java. Bây giờ, chúng ta sẽ xem xét cách sử dụng chúng và cách chúng hoạt động.


4. Thuốc generic hoạt động như thế nào

Trên thực tế, thuốc generic rất nguyên thủy.

Trình biên dịch chỉ cần thay thế các kiểu chung bằng các kiểu thông thường. Nhưng khi các phương thức của một kiểu chung được sử dụng, trình biên dịch sẽ thêm một toán tử typecast để truyền tham số cho các tham số kiểu:

Mã số Trình biên dịch làm gì
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList list = new ArrayList();
list.add(1);
list.add( (Integer) 1 );
int x = list.get(0);
int x = (Integer) list.get(0);
list.set(0, 10);
list.set(0, (Integer) 10);

Giả sử chúng ta có một phương thức tính tổng các số trong một tập hợp các số nguyên:

Mã số Trình biên dịch làm gì
public int sum(ArrayList<Integer> numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + numbers.get(i);

   return result;
}
public int sum(ArrayList numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + (Integer) numbers.get(i);

   return result;
}

Nói cách khác, thuốc generic là một loại đường cú pháp, giống như autoboxing, nhưng nhiều hơn một chút. Với autoboxing, trình biên dịch thêm các phương thức để chuyển đổi an intthành an Integervà ngược lại, và đối với generic, nó thêm các toán tử định kiểu.

Sau khi trình biên dịch biên dịch các lớp chung của bạn với các tham số kiểu, chúng chỉ được chuyển đổi thành các lớp thông thường và các toán tử định kiểu. Thông tin về các đối số kiểu được truyền cho các biến kiểu chung sẽ bị mất. Hiệu ứng này còn được gọi là xóa kiểu .

Đôi khi các lập trình viên viết các lớp chung (các lớp có tham số kiểu) thực sự cần thông tin về các kiểu được truyền dưới dạng đối số. Trong nhiệm vụ Bộ sưu tập Java, bạn sẽ học cách giải quyết vấn đề này và những gì nó đòi hỏi.



5. Một số thông tin về thuốc generic

Dưới đây là một số sự thật thú vị hơn về thuốc generic.

Các lớp có thể có một số tham số kiểu. Nó trông giống như thế này:

ClassName<TypeParameter1, TypeParameter2, TypeParameter3>

Trên thực tế, điều này không thực sự đáng ngạc nhiên. Bất cứ nơi nào trình biên dịch có thể thêm một toán tử để truyền thành một loại, nó có thể thêm nhiều toán tử truyền kiểu.

Ví dụ:

Mã số Ghi chú
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Hello");
map.put(-15, "Hello");
Tham số đầu tiên của phương putthức là một Integervà tham số thứ hai là mộtString

Các loại chung cũng có thể được sử dụng làm tham số . Nó trông giống như thế này:

ClassName<TypeParameter<TypeParameterParameter>>

Giả sử chúng ta muốn tạo một danh sách sẽ lưu trữ danh sách các chuỗi. Trong trường hợp này, chúng ta sẽ nhận được một cái gì đó như thế này:

// List of greetings
ArrayList<String> listHello = new ArrayList<String>();
listHello.add ("Hello");
listHello.add ("Hi");

// List of goodbyes
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Bye");
listBye.add ("Goodbye");

// List of lists
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);

Các kiểu chung (kiểu có tham số kiểu) cũng có thể được sử dụng làm kiểu mảng. Nó trông giống như thế này:

ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];

Không có gì kỳ diệu xảy ra ở đây: dấu ngoặc nhọn chỉ biểu thị tên loại:

Mã số đối tác không chung chung
ArrayList<String>[] list = new ArrayList<String>[10];
StringArrayList[] list = new StringArrayList[10];
ArrayList<Integer>[] list = new ArrayList<Integer>[10];
IntegerArrayList[] list = new IntegerArrayList[10];
ArrayList<Scanner>[] list = new ArrayList<Scanner>[10];
ScannerArrayList[] list = new ScannerArrayList[10];