Bài đăng nổi bật


Hướng Dẫn JAVA Core

Thừa Kế - Inheritance 


2.  Inheritance

Trong OOP, chúng tôi thường tổ chức các lớp theo thứ bậc để tránh trùng lặp và giảm sự dư thừa . Các lớp trong hệ thống phân cấp thấp hơn kế thừa tất cả các biến (thuộc tính tĩnh) và phương thức (hành vi động) từ cấu trúc phân cấp cao hơn. Một lớp trong hệ thống phân cấp thấp hơn được gọi là một lớp con (hoặc dẫn xuất , lớp con , lớp mở rộng ). Một lớp trong hệ thống phân cấp trên được gọi là siêu lớp (hoặc lớp cơ sở , lớp cha ). Bằng cách kéo ra tất cả các biến và phương thức phổ biến vào các siêu lớp và để lại các biến và phương thức chuyên biệt trong các lớp con, dự phòngcó thể được giảm hoặc loại bỏ rất nhiều vì các biến và phương thức phổ biến này không cần phải lặp lại trong tất cả các lớp con. Ví dụ,

OOP_InheritanceExamples.png
Một lớp con kế thừa tất cả các biến và phương thức từ các siêu lớp của nó, bao gồm cả cha mẹ trực tiếp của nó cũng như tất cả các tổ tiên. Điều quan trọng cần lưu ý là một lớp con không phải là "tập hợp con" của siêu lớp. Ngược lại, lớp con là "siêu lớp" của siêu lớp. Đó là bởi vì một lớp con kế thừa tất cả các biến và phương thức của lớp cha; Ngoài ra, nó mở rộng siêu lớp bằng cách cung cấp nhiều biến và phương thức hơn.
Trong Java, bạn định nghĩa một lớp con bằng cách sử dụng từ khóa " extends", ví dụ:
class Goalkeeper extends SoccerPlayer {......}
class MyApplet extends java.applet.Applet {.....}
class Cylinder extends Circle {......}
OOP_UMLSuperSubClass.png
Ký hiệu UML: Ký hiệu UML cho thừa kế là một đường liền nét với đầu mũi tên rỗng dẫn từ lớp con đến siêu lớp của nó. Theo quy ước, siêu lớp được vẽ trên đầu các lớp con của nó như được hiển thị.

2.1  Tính Thừa Kế EG. 1: Lớp Circle và lớp Cylinder

OOP_CircleCylinder.png
Trong ví dụ này, chúng ta có một lớp con Cylinder thừa kế từ lớp cha Circle, mà chúng ta đã tạo trong chương trước. Điều quan trọng cần lưu ý là chúng tôi sử dụng lại lớp CircleKhả năng sử dụng lại là một trong những tính chất quan trọng nhất của OOP. (Tại sao phát minh lại bánh xe?) Lớp Cylinder kế thừa tất cả các biến thành viên ( radius và color) và phương thức ( getRadius()getArea(), và những thứ khác nghĩa) từ lớp cha của nó CircleNó tiếp tục định nghĩa một biến gọi là height, hai phương thức - getHeight()và getVolume()và constructor riêng của nó, như:
Circle.java (Re-produced)
public class Circle {
   // private instance variables
   private double radius;
   private String color;

   // Constructors
   public Circle() {
      this.radius = 1.0;
      this.color = "red";
      System.out.println("Construced a Circle with Circle()");  // for debugging
   }
   public Circle(double radius) {
      this.radius = radius;
      this.color = "red";
      System.out.println("Construced a Circle with Circle(radius)");  // for debugging
   }
   public Circle(double radius, String color) {
      this.radius = radius;
      this.color = color;
      System.out.println("Construced a Circle with Circle(radius, color)");  // for debugging
   }

   // public getters and setters for the private variables
   public double getRadius() {
      return this.radius;
   }
   public String getColor() {
      return this.color;
   }
   public void setRadius(double radius) {
      this.radius = radius;
   }
   public void setColor(String color) {
      this.color = color;
   }

   /** Returns a self-descriptive String */
   public String toString() {
      return "Circle[radius=" + radius + ",color=" + color + "]";
   }

   /** Returns the area of this Circle */
   public double getArea() {
      return radius * radius * Math.PI;
   }
}
Cylinder.java
/**
 * A Cylinder is a Circle plus a height.
 */
public class Cylinder extends Circle {
   // private instance variable
   private double height;

   // Constructors
   public Cylinder() {
      super();  // invoke superclass' constructor Circle()
      this.height = 1.0;
      System.out.println("Constructed a Cylinder with Cylinder()");  // for debugging
   }
   public Cylinder(double height) {
      super();  // invoke superclass' constructor Circle()
      this.height = height;
      System.out.println("Constructed a Cylinder with Cylinder(height)");  // for debugging
   }
   public Cylinder(double height, double radius) {
      super(radius);  // invoke superclass' constructor Circle(radius)
      this.height = height;
      System.out.println("Constructed a Cylinder with Cylinder(height, radius)");  // for debugging
   }
   public Cylinder(double height, double radius, String color) {
      super(radius, color);  // invoke superclass' constructor Circle(radius, color)
      this.height = height;
      System.out.println("Constructed a Cylinder with Cylinder(height, radius, color)");  // for debugging
   }

   // Getter and Setter
   public double getHeight() {
      return this.height;
   }
   public void setHeight(double height) {
      this.height = height;
   }

   /** Returns the volume of this Cylinder */
   public double getVolume() {
      return getArea()*height;   // Use Circle's getArea()
   }

   /** Returns a self-descriptive String */
   public String toString() {
      return "This is a Cylinder";  // to be refined later
   }
}
Test Drive cho Lớp Cylinder (TestCylinder.java)
/**
 * A test driver for the Cylinder class.
 */
public class TestCylinder {
   public static void main(String[] args) {
      Cylinder cy1 = new Cylinder();
      //Construced a Circle with Circle()
      //Constructed a Cylinder with Cylinder()
      System.out.println("Radius is " + cy1.getRadius()
         + ", Height is " + cy1.getHeight()
         + ", Color is " + cy1.getColor()
         + ", Base area is " + cy1.getArea()
         + ", Volume is " + cy1.getVolume());
      //Radius is 1.0, Height is 1.0, Color is red,
      //Base area is 3.141592653589793, Volume is 3.141592653589793

      Cylinder cy2 = new Cylinder(5.0, 2.0);
      //Construced a Circle with Circle(radius)
      //Constructed a Cylinder with Cylinder(height, radius)
      System.out.println("Radius is " + cy2.getRadius()
         + ", Height is " + cy2.getHeight()
         + ", Color is " + cy2.getColor()
         + ", Base area is " + cy2.getArea()
         + ", Volume is " + cy2.getVolume());
      //Radius is 2.0, Height is 5.0, Color is red,
      //Base area is 12.566370614359172, Volume is 62.83185307179586
   }
}
Lưu ý ta cần đặt 2 lớp "Cylinder.java" và "TestCylinder.java" trong cùng thư mực với lớp "Circle.java" (bởi vì chúng ta đang sửa dụng lại class Circle). Biên dịch và chạy chương trình.

2.2  Method Overriding & Variable Hiding

Một lớp con kế thừa tất cả các biến và phương thức thành viên từ các siêu lớp của nó (cha mẹ trực tiếp và tất cả tổ tiên của nó). Class con có thể sử dụng các phương thức và biến được kế thừa như là của chính nó. Nó cũng có thể ghi đè một phương thức được kế thừa bằng cách cung cấp phiên bản của chính nó hoặc ẩn một biến được kế thừa bằng cách xác định một biến có cùng tên.
Ví dụ, phương thức được kế thừa getArea()trong một đối tượng Cylinder sẽ tính diện tích cơ sở của hình trụ. Giả sử rằng chúng ta quyết định ghi đè lên getArea()để tính diện tích bề mặt của hình trụ trong lớp con CylinderDưới đây là những thay đổi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Cylinder extends Circle {
   ......
   // Override the getArea() method inherited from superclass Circle
   @Override
   public double getArea() {
      return 2*Math.PI*getRadius()*height + 2*super.getArea();
   }
   // Need to change the getVolume() as well
   public double getVolume() {
      return super.getArea()*height;   // use superclass' getArea()
   }
   // Override the inherited toString()
   @Override
   public String toString() {
      return "Cylinder[" + super.toString() + ",height=" + height + "]";   
   }
}

Nếu getArea()được gọi từ một đối tượng Circle, nó sẽ tính diện tích của vòng tròn. Nếu getArea()được gọi từ một đối tượng Cylinder, nó sẽ tính diện tích bề mặt của hình trụ bằng cách sử dụng thực hiện ghi đè . Lưu ý rằng bạn phải sử dụng phương pháp accessor công cộng getRadius()để lấy radius của Circle, bởi vì radius được khai báo private và do đó không thể truy cập đến các lớp khác, kể cả các lớp con Cylinder.
Nhưng nếu bạn ghi đè getArea()trong Cylinder, thì getVolume()=getArea()*height) không còn hoạt động. Đó là bởi vì phần ghi đè getArea()sẽ được sử dụng Cylinder, không tính diện tích cơ sở. Bạn có thể khắc phục sự cố này bằng cách sử dụng super.getArea()để sử dụng phiên bản của siêu lớp getArea()Lưu ý rằng super.getArea()chỉ có thể được ban hành từ định nghĩa lớp con, nhưng không thể gọi từ một đối tượng được tạo, ví dụ c1.super.getArea(), vì nó phá vỡ nguyên tắc ẩn và đóng gói thông tin.

2.3  Annotation @Override (JDK 1.5)

@Override" Được gọi là chú thích - Annotation (được giới thiệu trong JDK 1.5), yêu cầu trình biên dịch kiểm tra xem liệu có một phương thức như vậy trong siêu lớp được ghi đè hay không. Điều này giúp ích rất nhiều nếu bạn viết sai tên của phương thức được ghi đè. Ví dụ: giả sử bạn muốn ghi đè phương thức toString()trong một lớp con. Nếu @Override không được sử dụng và toString()bị sai chính tả TOString(), nó sẽ được coi là một phương thức mới trong lớp con, thay vì ghi đè lên lớp cha. Nếu @Override được sử dụng, trình biên dịch sẽ báo hiệu lỗi.
@Override chú thích là tùy chọn, nhưng chắc chắn rằng nếu có thì sẽ hay hơn :) 
Chú thích không phải là cấu trúc lập trình. Nó không có ảnh hưởng đến đầu ra chương trình. Nó chỉ được sử dụng bởi trình biên dịch, loại bỏ sau khi biên dịch và không được sử dụng bởi bộ thực thi.

2.4  Keyword "super"

Hãy nhớ lại rằng bên trong một định nghĩa lớp, bạn có thể sử dụng từ khóa this để chỉ trường hợp này . Tương tự, từ khóa super đề cập đến siêu lớp, có thể là cha mẹ ngay cận kề hoặc tổ tiên của nó.
Từ khóa super cho phép lớp con truy cập các phương thức và biến của siêu lớp trong định nghĩa của lớp con. Ví dụ, super()và có thể được sử dụng gọi hàm tạo của lớp cha. Nếu lớp con ghi đè một phương thức được kế thừa từ siêu lớp của nó , bạn có thể sử dụng để gọi phiên bản của lớp cha trong định nghĩa của lớp con. Tương tự, nếu lớp con của bạn ẩn một trong các biến của lớp cha, bạn có thể sử dụng để tham chiếu đến biến ẩn trong định nghĩa của lớp con.super(argumentList)getArea()super.getArea()super.variableName

2.5  Bàn thêm về hàm tạo - Constructors

Hãy nhớ lại rằng lớp con kế thừa tất cả các biến và phương thức từ các siêu lớp của nó. Tuy nhiên, lớp con không kế thừa các hàm tạo của các siêu lớp của nó. Mỗi lớp trong Java định nghĩa các hàm tạo riêng của nó.
Trong phần thân của hàm tạo, bạn có thể sử dụng để gọi hàm tạo của siêu lớp ngay lập tức của nó. Lưu ý rằng , nếu nó được sử dụng, phải là câu lệnh đầu tiên trong hàm tạo của lớp con. Nếu nó không được sử dụng trong hàm tạo, trình biên dịch Java sẽ tự động chèn một câu lệnh để gọi hàm tạo không có đối số của siêu lớp ngay lập tức của nó. Điều này theo sau thực tế là cha mẹ phải được sinh ra trước khi đứa trẻ có thể được sinh ra. Bạn cần xây dựng các siêu lớp đúng cách trước khi bạn có thể xây dựng lớp con.super(args)super(args)super()

2.6  Default no-arg Constructor

Nếu không có hàm tạo nào được định nghĩa trong một lớp, trình biên dịch Java sẽ tự động tạo một hàm tạo không có đối số (không có đối số) , chỉ đơn giản là thực hiện một lời gọi super(), như sau:
// If no constructor is defined in a class, compiler inserts this no-arg constructor 
public ClassName () {  
   super();   // call the superclass' no-arg constructor
}
Hãy lưu ý rằng:
  • Hàm tạo không có đối số mặc định sẽ không được tạo tự động, nếu một (hoặc nhiều) hàm tạo được xác định. Nói cách khác, bạn cần xác định rõ ràng constructor không có đối số nếu các constructor khác được định nghĩa. 
  • Nếu siêu lớp ngay lập tức không có hàm tạo mặc định (nó xác định một số hàm tạo nhưng không xác định hàm tạo không có đối số), bạn sẽ gặp lỗi biên dịch khi thực hiện super()cuộc gọi. Lưu ý rằng trình biên dịch Java chèn một super()câu lệnh đầu tiên trong hàm tạo nếu không có super(args).

2.7  Single Inheritance

Java không hỗ trợ nhiều kế thừa (C ++ không). Nhiều kế thừa cho phép một lớp con có nhiều hơn một siêu lớp trực tiếp. Điều này có một nhược điểm nghiêm trọng nếu các siêu lớp có triển khai xung đột cho cùng một phương thức. Trong Java, mỗi lớp con có thể có một và chỉ một siêu lớp trực tiếp, nghĩa là thừa kế đơn. Mặt khác, một siêu lớp có thể có nhiều lớp con.

2.8  Common Root Class - java.lang.Object

Java áp dụng một cách tiếp cận cái gọi là gốc chung . Tất cả các lớp Java có nguồn gốc từ một lớp gốc chung được gọi là java.lang.ObjectObjectLớp này định nghĩa và thực hiện các hành vi phổ biến được yêu cầu đối với tất cả các đối tượng Java đang chạy trong JRE. Những hành vi phổ biến này cho phép thực hiện các tính năng như đa luồng và trình thu gom rác.

2.9  Inheritance EG. 2: The Point2D and Point3D Classes

OOP_PointPoint3D.png
The Superclass Point2D.java
/**
 * The Point2D class models a 2D point at (x, y).
 */
public class Point2D {
   // Private instance variables
   private int x, y;

   // Constructors
   /** Construct a Point2D instance at (0,0) */
   public Point2D() {  // default constructor
      this.x = 0;
      this.y = 0;
   }
   /** Constructs a Point2D instance at the given (x,y) */
   public Point2D(int x, int y) {
      this.x = x;
      this.y = y;
   }

   // Getters and Setters
   public int getX() {
      return this.x;
   }
   public void setX(int x) {
      this.x = x;
   }
   public int getY() {
      return this.y;
   }
   public void setY(int y) {
      this.y = y;
   }

   /** Returns a self-descriptive string in the form of "(x,y)" */
   @Override
   public String toString() {
      return "(" + this.x + "," + this.y + ")";
   }
}
The Subclass Point3D.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * The Point3D class models a 3D point at (x, y,z),
 * which is a subclass of Point2D.
 */
public class Point3D extends Point2D {
   // Private instance variables
   private int z;

   // Constructors
   /** Constructs a Point3D instance at (0,0,0) */
   public Point3D() {  // default constructor
      super();     // x = y = 0
      this.z = 0;
   }
   /** Constructs a Point3D instance at (x,y,z) */
   public Point3D(int x, int y, int z) {
      super(x, y);
      this.z = z;
   }

   // Getters and Setters
   public int getZ() {
      return this.z;
   }
   public void setZ(int z) {
      this.z = z;
   }

   /** Returns a self-descriptive string in the form of "(x,y,z)" */
   @Override
   public String toString() {
      return "(" + super.getX() + "," + super.getY() + "," + this.z + ")";
   }
}
A Test Driver for Point2D and Point3D Classes (TestPoint2DPoint3D.java)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * A test driver for the Point2D and Point3D classes
 */
public class TestPoint2DPoint3D {
   public static void main(String[] args) {
      /* Test Point2D */
      // Test constructors and toString()
      Point2D p2a = new Point2D(1, 2);
      System.out.println(p2a);  // toString()
      Point2D p2b = new Point2D();  // default constructor
      System.out.println(p2b);
      // Test Setters and Getters
      p2a.setX(3);  // Test setters
      p2a.setY(4);
      System.out.println(p2a);  // toString()
      System.out.println("x is: " + p2a.getX());
      System.out.println("x is: " + p2a.getY());

      /* Test Point3D */
      // Test constructors and toString()
      Point3D p3a = new Point3D(11, 12, 13);
      System.out.println(p3a);  // toString()
      Point2D p3b = new Point3D();  // default constructor
      System.out.println(p3b);
      // Test Setters and Getters
      p3a.setX(21);  // in superclass
      p3a.setY(22);  // in superclass
      p3a.setZ(23);  // in this class
      System.out.println(p3a);  // toString()
      System.out.println("x is: " + p3a.getX());  // in superclass
      System.out.println("y is: " + p3a.getY());  // in superclass
      System.out.println("z is: " + p3a.getZ());  // in this class
   }
}

2.10  Inheritance EG. 3: Superclass Person and its Subclasses

OOP_PersonStudnetTeacher.png
Giả sử rằng chúng tôi được yêu cầu làm mẫu cho học sinh và giáo viên trong ứng dụng của chúng tôi. Chúng ta có thể định nghĩa một lớp cha gọi Personđến thuộc tính lưu trữ thông thường như namevà address, và các lớp con Studentvà Teachercho các thuộc tính cụ thể của họ. Đối với sinh viên, chúng tôi cần duy trì các khóa học đã thực hiện và điểm số tương ứng của họ; thêm một khóa học với lớp, in tất cả các khóa học và lớp trung bình. Giả sử rằng một sinh viên học không quá 30 khóa học cho toàn bộ chương trình. Đối với giáo viên, chúng tôi cần duy trì các khóa học hiện tại và có thể thêm hoặc xóa một khóa học được giảng dạy. Giả sử rằng một giáo viên dạy không quá 5 khóa học đồng thời.
Chúng tôi thiết kế các lớp như sau.

The Superclass Person.java
/**
 * The superclass Person has name and address.
 */
public class Person {
   // private instance variables
   private String name, address;
   
   /** Constructs a Person instance with the given name and address */
   public Person(String name, String address) {
      this.name = name;
      this.address = address;
   }
   
   // Getters and Setters
   /** Returns the name */
   public String getName() {
      return name;
   }
   /** Returns the address */
   public String getAddress() {
      return address;
   }
   /** Sets the address */
   public void setAddress(String address) {
      this.address = address;
   }
   
   /** Returns a self-descriptive string */
   @Override
   public String toString() {
      return name + "(" + address + ")";
   }
}
The Subclass Student.java
/**
 * The Student class, subclass of Person.
 */
public class Student extends Person {
   // private instance variables
   private int numCourses;   // number of courses taken so far
   private String[] courses; // course codes
   private int[] grades;     // grade for the corresponding course codes
   private static final int MAX_COURSES = 30; // maximum number of courses
   
   /** Constructs a Student instance with the given name and address */
   public Student(String name, String address) {
      super(name, address);
      numCourses = 0;
      courses = new String[MAX_COURSES];
      grades = new int[MAX_COURSES];
   }
   
   /** Returns a self-descriptive string */
   @Override
   public String toString() {
      return "Student: " + super.toString();
   }
   
   /** Adds a course and its grade - No input validation */
   public void addCourseGrade(String course, int grade) {
      courses[numCourses] = course;
      grades[numCourses] = grade;
      ++numCourses;
   }
   
   /** Prints all courses taken and their grade */
   public void printGrades() {
      System.out.print(this);
      for (int i = 0; i < numCourses; ++i) {
         System.out.print(" " + courses[i] + ":" + grades[i]);
      }
      System.out.println();
   }
   
   /** Computes the average grade */
   public double getAverageGrade() {
      int sum = 0;
      for (int i = 0; i < numCourses; i++ ) {
         sum += grades[i];
      }
      return (double)sum/numCourses;
   }
}
The Subclass Teacher.java
/**
 * The Teacher class, subclass of Person.
 */
public class Teacher extends Person {
   // private instance variables
   private int numCourses;   // number of courses taught currently
   private String[] courses; // course codes
   private static final int MAX_COURSES = 5; // maximum courses
   
   /** Constructs a Teacher instance with the given name and address */
   public Teacher(String name, String address) {
      super(name, address);
      numCourses = 0;
      courses = new String[MAX_COURSES];
   }
   
   /** Returns a self-descriptive string */
   @Override
   public String toString() {
      return "Teacher: " + super.toString();
   }
   
   /** Adds a course. Returns false if the course has already existed */
   public boolean addCourse(String course) {
      // Check if the course already in the course list
      for (int i = 0; i < numCourses; i++) {
         if (courses[i].equals(course)) return false;
      }
      courses[numCourses] = course;
      numCourses++;
      return true;
   }
   
   /** Removes a course. Returns false if the course cannot be found in the course list */
   public boolean removeCourse(String course) {
      boolean found = false;
      // Look for the course index
      int courseIndex = -1;  // need to initialize
      for (int i = 0; i < numCourses; i++) {
         if (courses[i].equals(course)) {
            courseIndex = i;
            found = true;
            break;
         }
      }
      if (found) {
         // Remove the course and re-arrange for courses array
         for (int i = courseIndex; i < numCourses-1; i++) {
            courses[i] = courses[i+1];
         }
         numCourses--;
         return true;
      } else {
         return false;
      }
   }
}
A Test Driver (TestPerson.java)
/**
 * A test driver for Person and its subclasses.
 */
public class TestPerson {
   public static void main(String[] args) {
      /* Test Student class */
      Student s1 = new Student("Tan Ah Teck", "1 Happy Ave");
      s1.addCourseGrade("IM101", 97);
      s1.addCourseGrade("IM102", 68);
      s1.printGrades();
      //Student: Tan Ah Teck(1 Happy Ave) IM101:97 IM102:68
      System.out.println("Average is " + s1.getAverageGrade());
      //Average is 82.5

      /* Test Teacher class */
      Teacher t1 = new Teacher("Paul Tan", "8 sunset way");
      System.out.println(t1);
      //Teacher: Paul Tan(8 sunset way)
      String[] courses = {"IM101", "IM102", "IM101"};
      for (String course: courses) {
         if (t1.addCourse(course)) {
            System.out.println(course + " added");
         } else {
            System.out.println(course + " cannot be added");
         }
      }
      //IM101 added
      //IM102 added
      //IM101 cannot be added
      for (String course: courses) {
         if (t1.removeCourse(course)) {
            System.out.println(course + " removed");
         } else {
            System.out.println(course + " cannot be removed");
         }
      }
      //IM101 removed
      //IM102 removed
      //IM101 cannot be removed
   }
}

2.11  Exercises

LINK TO EXERCISES

3.  Composition vs. Inheritance

3.1  "Một dòng bao gồm 2 điểm" so với "Một dòng là một điểm được mở rộng bởi một điểm khác""

Hãy nhớ lại rằng có hai cách sử dụng lại các lớp hiện có: thành phần và kế thừa . Chúng ta đã thấy rằng một Linelớp có thể được thực hiện bằng cách sử dụng thành phần của Pointlớp - "Một dòng bao gồm hai điểm", trong phần trước.
Linecũng có thể được thực hiện, sử dụng tính kế thừa từ Pointlớp - "Một dòng là một điểm được mở rộng bởi một điểm khác". Hãy gọi lớp con này LineSub(để phân biệt với Linelớp sử dụng thành phần).
OOP_PointLineSub.png
The Superclass Point.java
như trên
The Subclass LineSub.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
 * The LineSub class, subclass of Point.
 * It inherits the begin point from the superclass, and adds an end point.
 */
public class LineSub extends Point {  // Inherited the begin point
   // Private instance variables
   Point end;   // Declare end as instance of Point
 
   /** Constructs a LineSub instance with 2 points at (x1, y1) and (x2, y2) */
   public LineSub(int x1, int y1, int x2, int y2) {
      super(x1, y1);
      this.end = new Point(x2, y2);   // Construct Point instances
   }
   /** Constructs a LineSub instance with the 2 given Point instances */
   public LineSub(Point begin, Point end) {
      super(begin.getX(), begin.getY());  // Need to construct super
      this.end = end;
   }

   // Getters and Setters 
   public Point getBegin() {
      return this;   // upcast to Point (polymorphism - to be discussed later)
   }
   public Point getEnd() {
      return end;
   }
   public void setBegin(Point begin) {
      super.setX(begin.getX());
      super.setY(begin.getY());
   }
   public void setEnd(Point end) {
      this.end = end;
   }
 
   // Other Get and Set methods
   public int getBeginX() {
      return super.getX();  // inherited, super is optional
   }
   public void setBeginX(int x) {
      super.setX(x);        // inherited, super is optional
   }
   public int getBeginY() {
      return super.getY();
   }
   public void setBeginY(int y) {
      super.setY(y);
   }
   public int[] getBeginXY() {
      return super.getXY();
   }
   public void setBeginXY(int x, int y) {
      super.setXY(x, y);
   }
   public int getEndX() {
      return end.getX();
   }
   public void setEndX(int x) {
      end.setX(x);
   }
   public int getEndY() {
      return end.getY();
   }
   public void setEndY(int y) {
      end.setY(y);
   }
   public int[] getEndXY() {
      return end.getXY();
   }
   public void setEndXY(int x, int y) {
      end.setXY(x, y);
   }
 
   /** Returns a self-descriptive string */
   public String toString() {
      return "LineSub[begin=" + super.toString() + ",end=" + end + "]";
   }
 
   /** Returns the length of this line */
   public double getLength() {
      return super.distance(end);
   }
}
A Test Driver (TestLineSub.java)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * Test Driver for the LineSub class
 */
public class TestLineSub {
   public static void main(String[] args) {
      // Test constructors and toString()
      LineSub l1 = new LineSub(1, 2, 3, 4);
      System.out.println(l1);  // toString()
      LineSub l2 = new LineSub(new Point(5,6), new Point(7,8));
      System.out.println(l2);

      // Test Setters and Getters
      l1.setBegin(new Point(11, 12));
      l1.setEnd(new Point(13, 14));
      System.out.println(l1);  // toString()
      System.out.println("begin is: " + l1.getBegin());
      System.out.println("end is: " + l1.getEnd());

      l1.setBeginX(21);
      l1.setBeginY(22);
      l1.setEndX(23);
      l1.setEndY(24);
      System.out.println(l1);
      System.out.println(l1);  // toString()
      System.out.println("begin's x is: " + l1.getBeginX());
      System.out.println("begin's y is: " + l1.getBeginY());
      System.out.println("end's x is: " + l1.getEndX());
      System.out.println("end's y is: " + l1.getEndY());

      l1.setBeginXY(31, 32);
      l1.setEndXY(33, 34);
      System.out.println(l1);  // toString()
      System.out.println("begin's x is: " + l1.getBeginXY()[0]);
      System.out.println("begin's y is: " + l1.getBeginXY()[1]);
      System.out.println("end's x is: " + l1.getEndXY()[0]);
      System.out.println("end's y is: " + l1.getEndXY()[1]);

      // Test getLength()
      System.out.printf("length is: %.2f%n", l1.getLength());
   }
}
Lưu ý: Đây là trình điều khiển thử nghiệm tương tự được sử dụng trong ví dụ trước về thành phần, ngoại trừ thay đổi tên lớp.
Nghiên cứu cả hai phiên bản của lớp Line ( Linevà LineSub). Tôi cho rằng sẽ dễ dàng hơn khi nói rằng "Một dòng gồm hai điểm" so với "Một dòng là một điểm được mở rộng bởi một điểm khác".
Nguyên tắc chung: Sử dụng thành phần nếu có thể, trước khi xem xét kế thừa. Chỉ sử dụng kế thừa nếu có mối quan hệ phân cấp rõ ràng giữa các lớp.

Post a Comment

أحدث أقدم