← Назад к вопросам

Для чего нужен extends?

1.0 Junior🔥 171 комментариев
#ООП

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Для чего нужен extends?

Основное назначение

extends — это ключевое слово в Java для наследования (inheritance), которое позволяет одному классу (подклассу/потомку) получить все поля и методы другого класса (суперкласса/родителя).

Это один из трёх столпов объектно-ориентированного программирования (наряду с инкапсуляцией и полиморфизмом).

Синтаксис

public class Parent {
    public void parentMethod() {
        System.out.println("Parent method");
    }
}

public class Child extends Parent {
    // Наследует parentMethod() от Parent
    // Может переопределить (override) parentMethod()
    
    @Override
    public void parentMethod() {
        System.out.println("Child method");
    }
}

// Использование
Child child = new Child();
child.parentMethod();  // Выведет: "Child method"

Parent parent = new Parent();
parent.parentMethod();  // Выведет: "Parent method"

Основные причины использования extends

1. Повторное использование кода (Code Reuse)

// БЕЗ extends - дублирование кода
class Dog {
    public void eat() { System.out.println("Eating"); }
    public void sleep() { System.out.println("Sleeping"); }
    public void bark() { System.out.println("Bark"); }
}

class Cat {
    public void eat() { System.out.println("Eating"); }   // Дублирование!
    public void sleep() { System.out.println("Sleeping"); }  // Дублирование!
    public void meow() { System.out.println("Meow"); }
}

// С extends - общий код в одном месте
class Animal {
    public void eat() { System.out.println("Eating"); }
    public void sleep() { System.out.println("Sleeping"); }
}

class Dog extends Animal {
    public void bark() { System.out.println("Bark"); }
}

class Cat extends Animal {
    public void meow() { System.out.println("Meow"); }
}

2. Иерархия классов (IS-A отношение)

// Естественная иерархия
class Vehicle {
    private String color;
    private double speed;
    
    public void move() { /* ... */ }
}

class Car extends Vehicle {
    private int numDoors;
    
    public void openTrunk() { /* ... */ }
}

class Bicycle extends Vehicle {
    private boolean hasBrakes;
    
    public void ringBell() { /* ... */ }
}

// Использование
Car car = new Car();          // car IS-A Vehicle
Bicycle bike = new Bicycle(); // bike IS-A Vehicle

// Полиморфизм
Vehicle vehicle1 = car;   // Car может быть использована как Vehicle
Vehicle vehicle2 = bike;  // Bicycle может быть использована как Vehicle

3. Полиморфизм

class Shape {
    public void draw() {
        System.out.println("Drawing shape");
    }
    
    public double area() {
        return 0;
    }
}

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing circle");
    }
    
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

class Square extends Shape {
    private double side;
    
    public Square(double side) {
        this.side = side;
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing square");
    }
    
    @Override
    public double area() {
        return side * side;
    }
}

// Полиморфная обработка
List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle(5));
shapes.add(new Square(4));

for (Shape shape : shapes) {
    shape.draw();  // Вызывает переопределённый метод
    System.out.println("Area: " + shape.area());
}

// Вывод:
// Drawing circle
// Area: 78.53981...
// Drawing square
// Area: 16.0

Практические примеры

1. Exception иерархия

// Все исключения наследуют Throwable
class Throwable { /* ... */ }

class Exception extends Throwable { /* ... */ }

class RuntimeException extends Exception { /* ... */ }

class NullPointerException extends RuntimeException { /* ... */ }

class IllegalArgumentException extends RuntimeException { /* ... */ }

// Использование
try {
    // code
} catch (Exception e) {  // Ловит все Exception и его подклассы
    e.printStackTrace();
}

2. Collection Framework

// Иерархия Collections
interface Collection<E> { /* ... */ }

interface List<E> extends Collection<E> { /* ... */ }

abstract class AbstractList<E> implements List<E> { /* ... */ }

class ArrayList<E> extends AbstractList<E> { /* ... */ }

class LinkedList<E> extends AbstractList<E> { /* ... */ }

// Использование полиморфизма
List<String> list = new ArrayList<>();  // Можно заменить на LinkedList
list.add("item");

3. Entity иерархия в JPA

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Animal {
    @Id
    private Long id;
    private String name;
    
    public void eat() { /* ... */ }
}

@Entity
public class Dog extends Animal {
    private String breed;
    
    public void bark() { /* ... */ }
}

@Entity
public class Cat extends Animal {
    private boolean indoor;
    
    public void meow() { /* ... */ }
}

4. Service слой

public abstract class BaseService<T> {
    protected Repository<T> repository;
    
    public void save(T entity) {
        repository.save(entity);
    }
    
    public T findById(Long id) {
        return repository.findById(id).orElse(null);
    }
    
    public List<T> findAll() {
        return repository.findAll();
    }
}

@Service
public class UserService extends BaseService<User> {
    @Autowired
    private UserRepository userRepository;
    
    @Override
    protected Repository<User> getRepository() {
        return userRepository;
    }
    
    public User findByEmail(String email) {
        return userRepository.findByEmail(email);
    }
}

@Service
public class ProductService extends BaseService<Product> {
    @Autowired
    private ProductRepository productRepository;
    
    @Override
    protected Repository<Product> getRepository() {
        return productRepository;
    }
    
    public List<Product> findByCategory(String category) {
        return productRepository.findByCategory(category);
    }
}

Переопределение (Override) vs Перегрузка (Overload)

class Parent {
    public void method(String s) {
        System.out.println("Parent method");
    }
}

class Child extends Parent {
    // ПЕРЕОПРЕДЕЛЕНИЕ (Override) - тот же сигнатура
    @Override
    public void method(String s) {
        System.out.println("Child method - override");
    }
    
    // ПЕРЕГРУЗКА (Overload) - другая сигнатура
    public void method(int i) {
        System.out.println("Child method - overload");
    }
}

Child child = new Child();
child.method("text");   // Вызовет переопределённый метод
child.method(5);        // Вызовет перегруженный метод

super vs this

class Parent {
    protected String name = "Parent";
    
    public void greet() {
        System.out.println("Hello from Parent");
    }
}

class Child extends Parent {
    private String name = "Child";
    
    @Override
    public void greet() {
        super.greet();  // Вызывает метод родителя
        System.out.println("Hello from Child");
    }
    
    public void printNames() {
        System.out.println(this.name);   // "Child"
        System.out.println(super.name);  // "Parent"
    }
}

Child child = new Child();
child.greet();
// Hello from Parent
// Hello from Child

child.printNames();
// Child
// Parent

Правила наследования

1. Java поддерживает ТОЛЬКО однократное наследование
   ✓ class Child extends Parent { }
   ✗ class Child extends Parent1, Parent2 { }  // ОШИБКА!
   
   // Для множественного наследования используют интерфейсы
   class Child extends Parent implements Interface1, Interface2 { }

2. Конструктор родителя НЕ наследуется явно
   class Child extends Parent {
       public Child() {
           super();  // НУЖНО вызвать конструктор родителя
       }
   }

3. Приватные методы НЕ наследуются
   class Parent {
       private void privateMethod() { }  // Не видна в Child
   }

4. Статические методы НЕ переопределяются (скрываются)
   class Parent {
       public static void staticMethod() { }
   }
   
   class Child extends Parent {
       public static void staticMethod() { }  // Не override, а hiding
   }

5. Нельзя сузить видимость при переопределении
   class Parent {
       public void method() { }  // public
   }
   
   class Child extends Parent {
       protected void method() { }  // ОШИБКА! Нельзя сузить видимость
   }

Когда НЕ использовать extends

✗ НЕ используй extends когда:
  1. HAS-A отношение (композиция лучше)
  
  // Плохо
  class Person extends ArrayList { }  // Человек НЕ ЯВЛЯЕТСЯ списком!
  
  // Хорошо
  class Person {
      private List<String> hobbies = new ArrayList<>();
  }

  2. Нужна гибкость (используй интерфейсы)
  
  // Плохо
  class MyClass extends BaseClass { }
  
  // Хорошо
  class MyClass implements MyInterface { }

  3. Только для перегруппировки кода (используй композицию)

Итог

extends используется для:

  1. Повторного использования кода — наследование методов и полей
  2. Создания иерархии классов — IS-A отношения
  3. Реализации полиморфизма — один объект может выступать разными типами
  4. Применения наследования при дизайне — создание расширяемых и гибких систем
  5. DRY принципа — не дублируй код, наследуй

Наследование — это мощный инструмент, но его нужно использовать правильно. Часто лучше использовать интерфейсы (implements) или композицию вместо наследования (extends).