Home Java javaTutorial Inheritance example analysis in Java

Inheritance example analysis in Java

Apr 20, 2023 pm 05:19 PM
java

 Interface (Interface) and Class (Class)?
Once, I attended a meeting of a Java user group. At the conference, James Gosling (the father of Java) gave the initiator speech. In that memorable Q&A segment, he was asked: "If you refactored Java, what would you change?". "I want to get rid of classes" he replied. After the laughter subsided, it was explained that the real problem was not the class itself, but the implementation of the inheritance relationship. Interface inheritance (implements relationship) is better. You should avoid implementing inheritance as much as possible.
Loss of flexibility
Why should you avoid inheritance? The first problem is that explicitly using concrete class names locks you into a specific implementation, making underlying changes unnecessarily difficult.
In the current agile programming method, the core is the concept of parallel design and development. You start programming before you design the program in detail. This technique differs from the traditional approach - where design should be completed before coding begins - but many successful projects have proven that you can develop high-quality code more quickly than with traditional step-by-step methods. method. But at the core of parallel development is flexibility. You have to write your code in a way so that newly discovered requirements can be merged into existing code as painlessly as possible.
Rather than implementing features you may need, you only need to implement features you clearly need, and be moderately tolerant of change. If you don't have this kind of flexible, parallel development, it's simply impossible.

Programming for Inteface is the core of flexible structures. To illustrate why, let's take a look at what happens when they are used. Consider the following code:

 f()
 {
 LinkedList list = new LinkedList();
 //...
 g( list );
 }

g(LinkedList list)
 {
list.add(...);
g2(list)
 }

Assume a need for fast query is raised so that this LinkedList cannot be resolved. You need to use HashSet instead. In existing code, changes cannot be localized, because you need to modify not only f() but also g() (which takes a LinkedList parameter), and any code that g() passes the list to. Rewrite the code as follows:

 f()
 {
 Collection list = new LinkedList();
 //...
 g( list );
}

 g( Collection list )
 {
 list.add( ... );
 g2( list )
 }

Modify the Linked list like this hash, may simply use new HashSet() instead of new LinkedList(). that's all. There is nothing else to modify.

As another example, compare the following two pieces of code:

f()
{
Collection c = new HashSet();
//...
 g( c );
 }

 g( Collection c )
 {
 for( Iterator i = c.iterator(); i.hasNext() )
 do_something_with ( i.next() );
 }

 and

 f2()
 {
 Collection c = new HashSet();
 //.. .
 g2( c.iterator() );
 }

 g2( Iterator i )
 {
 while( i.hasNext() )
 do_something_with( i. next() );
 }
The g2() method can now iterate over Collection derivation, just like you can get key-value pairs from a Map. In fact, you can write iterators that generate data instead of traversing a Collection. You can write iterators that get information from the test framework or file. This allows for tremendous flexibility.

 Coupling
For implementing inheritance, a more critical issue is coupling---annoying dependence, that is, the dependence of one part of the program on another part. Global variables provide a classic example of why strong coupling can cause trouble. For example, if you change the type of a global variable, then all functions that use that variable may be affected, so all of this code will have to be inspected, changed, and retested. Moreover, all functions that use this variable are coupled to each other through this variable. That is, if a variable value is changed at a time when it is difficult to use, one function may incorrectly affect the behavior of another function. This problem is significantly hidden in multi-threaded programs.
As a designer, you should strive to minimize coupling relationships. You can't eliminate coupling altogether, because method calls from objects of one class to objects of another class are a form of loose coupling. You can't have a program without any coupling. However, you can minimize some coupling by following OO rules (most importantly, the implementation of an object should be completely hidden from the objects that use it). For example, an object's instance variables (fields that are not constants) should always be private. I mean for a certain period of time, without exception, continuously. (You can occasionally use protected methods effectively, but protected instance variables are an abomination.) For the same reason you shouldn't use get/set functions --- they just feel overly complex for being public to a domain (despite the return modifier The reason for accessing functions of objects rather than primitive values ​​is in some cases, and in that case the returned object class is a key abstraction in the design).

Here, I am not bookish. In my own work, I find a direct correlation between the rigor of my OO approach, rapid code development, and easy code implementation. Whenever I violate central OO principles, such as implementation hiding, I end up rewriting that code (usually because the code is not debuggable). I don't have time to rewrite the code, so I follow those rules. Am I concerned about purely practical reasons? I'm not interested in clean reasons.

  The fragile base class problem

Now, let’s apply the concept of coupling to inheritance. In an implementation system that uses extends, the derived class is very tightly coupled to the base class, and this tight coupling is undesirable. Designers have applied the nickname "fragile base class problem" to describe this behavior. Base classes are considered fragile because you modify the base class seemingly safely, but when inheriting from a derived class, the new behavior may cause the derived class to become dysfunctional. You can't tell whether changes to a base class are safe by simply inspecting the base class in isolation; rather, you must also look at (and test) all derived classes. Furthermore, you must check all code that is also used in base and derived class objects, because this code may be broken by the new behavior. A simple change to a base class can render the entire program inoperable.

Let’s examine the problem of fragile base classes and base class coupling. The following class extends Java's ArrayList class to make it behave like a stack:

 class Stack extends ArrayList
 {
 private int stack_pointer = 0;

 public void push ( Object article )
 {
 add( stack_pointer , article );
 }

 public Object pop()
 {
 return remove( --stack_pointer );
 }

 public void push_many(Object[] articles)
 {
 for( int i = 0; i < articles.length; i )
  push( articles[i] ) ;
 }
 }

Even a simple class like this has problems. Consider when a user balances inheritance and uses ArrayList's clear() method to pop the stack:

 Stack a_stack = new Stack();
 a_stack.push("1");
 a_stack. push("2");
a_stack.clear();

This code compiles successfully, but because the base class does not know about the stack pointer stack, the stack object is currently in an undefined state . The next call to push() puts the new item at index 2. (the current value of stack_pointer), so the stack effectively has three elements - the bottom two are garbage. (Java's stack class has this problem, don't use it).

The solution to this annoying inherited method problem is to override all ArrayList methods for Stack, which can modify the state of the array. , so overwrite the correct operation of the Stack pointer or throw an exception. (The removeRange() method is a good candidate for throwing an exception).

This method has two disadvantages. First, if you cover everything, the base class should really be an interface, not a class. If you don't use any inherited methods, there is no point in implementing inheritance. Second, and more importantly, you cannot have a stack support all ArrayList methods. For example, the annoying removeRange() does nothing. The only reasonable way to implement a useless method is to make it throw an exception, since it should never be called. This method effectively turns compilation errors into runtime errors. The bad thing is that if the method is simply not defined, the compiler will print a method not found error. If a method exists but throws an exception, you won't find out about the calling error until the program is actually running.

A better solution to this base class problem is to encapsulate the data structure instead of using inheritance. This is the new and improved version of Stack:
 class Stack
 {
 private int stack_pointer = 0;
 private ArrayList the_data = new ArrayList();

 public void push ( Object article )
 {
 the_data.add( stack_poniter , article );
 }

 public Object pop()
 {
 return the_data.remove( --stack_pointer );
 }

 public void push_many( Object[] articles )
 {
 for( int i = 0; i < o.length; i )
  push( articles [i] );
 }
 }

So far, so good, but considering the fragile base class problem, let's say you want to create a variable in the stack and use it in a section Tracks the maximum stack size during the cycle. A possible implementation might look like this:

 class Monitorable_stack extends Stack
 {
 private int high_water_mark = 0;
 private int current_size;

 public void push( Object article )
 {
 if( current_size > high_water_mark )
 high_water_mark = current_size;
 super.push( article );
 }

 publish Object pop()
 {
 --current_size;
 return super.pop();
 }

 public int maximum_size_so_far()
 {
 return high_water_mark;
 }
 }
This new class worked just fine, at least for a while. Unfortunately, this code exploits the fact that push_many() operates by calling push(). First of all, this detail doesn't seem like a bad choice. It simplifies the code, and you can get the derived version of push() even when Monitorable_stack is accessed through a Stack reference, so that high_water_mark is updated correctly.

The above is the detailed content of Inheritance example analysis in Java. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Perfect Number in Java Perfect Number in Java Aug 30, 2024 pm 04:28 PM

Guide to Perfect Number in Java. Here we discuss the Definition, How to check Perfect number in Java?, examples with code implementation.

Weka in Java Weka in Java Aug 30, 2024 pm 04:28 PM

Guide to Weka in Java. Here we discuss the Introduction, how to use weka java, the type of platform, and advantages with examples.

Smith Number in Java Smith Number in Java Aug 30, 2024 pm 04:28 PM

Guide to Smith Number in Java. Here we discuss the Definition, How to check smith number in Java? example with code implementation.

Java Spring Interview Questions Java Spring Interview Questions Aug 30, 2024 pm 04:29 PM

In this article, we have kept the most asked Java Spring Interview Questions with their detailed answers. So that you can crack the interview.

Break or return from Java 8 stream forEach? Break or return from Java 8 stream forEach? Feb 07, 2025 pm 12:09 PM

Java 8 introduces the Stream API, providing a powerful and expressive way to process data collections. However, a common question when using Stream is: How to break or return from a forEach operation? Traditional loops allow for early interruption or return, but Stream's forEach method does not directly support this method. This article will explain the reasons and explore alternative methods for implementing premature termination in Stream processing systems. Further reading: Java Stream API improvements Understand Stream forEach The forEach method is a terminal operation that performs one operation on each element in the Stream. Its design intention is

TimeStamp to Date in Java TimeStamp to Date in Java Aug 30, 2024 pm 04:28 PM

Guide to TimeStamp to Date in Java. Here we also discuss the introduction and how to convert timestamp to date in java along with examples.

Java Program to Find the Volume of Capsule Java Program to Find the Volume of Capsule Feb 07, 2025 am 11:37 AM

Capsules are three-dimensional geometric figures, composed of a cylinder and a hemisphere at both ends. The volume of the capsule can be calculated by adding the volume of the cylinder and the volume of the hemisphere at both ends. This tutorial will discuss how to calculate the volume of a given capsule in Java using different methods. Capsule volume formula The formula for capsule volume is as follows: Capsule volume = Cylindrical volume Volume Two hemisphere volume in, r: The radius of the hemisphere. h: The height of the cylinder (excluding the hemisphere). Example 1 enter Radius = 5 units Height = 10 units Output Volume = 1570.8 cubic units explain Calculate volume using formula: Volume = π × r2 × h (4

Create the Future: Java Programming for Absolute Beginners Create the Future: Java Programming for Absolute Beginners Oct 13, 2024 pm 01:32 PM

Java is a popular programming language that can be learned by both beginners and experienced developers. This tutorial starts with basic concepts and progresses through advanced topics. After installing the Java Development Kit, you can practice programming by creating a simple "Hello, World!" program. After you understand the code, use the command prompt to compile and run the program, and "Hello, World!" will be output on the console. Learning Java starts your programming journey, and as your mastery deepens, you can create more complex applications.

See all articles