Строка ошибки «Ссылка на объект, не установленная на экземпляр объекта.» указывает, что вы не назначили экземпляр объекта объектной ссылке, и все же вы получаете доступ к свойствам / методам этого объекта.
например: скажем, у вас есть класс под названием myClass и он содержит одно свойство prop1.
public Class myClass
{
public int prop1 {get;set;}
}
Теперь вы получаете доступ к этому prop1 в каком-то другом классе, как показано ниже:
public class Demo
{
public void testMethod()
{
myClass ref = null;
ref.prop1 = 1; //This line throws error
}
}
выше строки выдает ошибку, потому что ссылка класса myClass объявлена, но не создана, или экземпляр объекта не назначается referecne этого класса.
Чтобы исправить это, вам нужно создать экземпляр (присвоить объект ссылке на этот класс).
public class Demo
{
public void testMethod()
{
myClass ref = null;
ref = new myClass();
ref.prop1 = 1;
}
}
Они совершенно разные. Наследование - это отношение "is-a" . Композиция представляет собой «has-a» .
Вы выполняете композицию, имея экземпляр другого класса C
в качестве поля вашего класса, вместо расширения C
. Хорошим примером, где состав был бы намного лучше, чем наследование, является java.util.Stack
, который в настоящее время распространяется java.util.Vector
. Это сейчас считается ошибкой. Стек "- это-НЕ-a" вектор; вам не разрешается вставлять и удалять элементы произвольно. Это должно было быть композиция.
К сожалению, слишком поздно исправить эту ошибку дизайна, так как изменение иерархии наследования теперь нарушит совместимость с существующим кодом. Если вместо наследования использовалась композиция Stack
, ее всегда можно было изменить, чтобы использовать другую структуру данных, не нарушая API .
Я очень рекомендую книгу Джоша Блоха Эффективная Java 2-е издание
Хороший объектно-ориентированный дизайн - это не либеральное расширение существующих классов. Ваш первый инстинкт должен составлять вместо этого.
См. Также:
Элементы композиции HAS A
Наследование означает IS A
Example
: автомобиль имеет двигатель и автомобиль - это автомобиль
. При программировании это выглядит как:
class Engine {} // The Engine class.
class Automobile {} // Automobile class which is parent to Car class.
class Car extends Automobile { // Car is an Automobile, so Car class extends Automobile class.
private Engine engine; // Car has an Engine so, Car class has an instance of Engine class as its member.
}
в качестве другого примера, рассмотрим класс автомобилей, это будет хорошим использованием композиции, автомобиль будет иметь «двигатель», коробку передач, шины, сиденья и т. д. Он не будет распространять ни один из этих классов.
Являются ли композиция и наследование одинаковыми?
Они не совпадают.
Композиция : позволяет группе объектов обрабатываться так же, как один экземпляр объекта. Цель композита состоит в том, чтобы «компоновать» объекты в древовидных структурах представлять целые иерархии
Inheritance : класс наследует поля и методы из все его суперклассы, прямые или косвенные. Подкласс может переопределять методы, которые он наследует, или он может скрыть поля или методы, которые он наследует.
Если я хочу реализовать шаблон композиции, как я могу это сделать на Java?
Статья в Википедии достаточно хороша для реализации составного шаблона в java.
Ключевые участники:
Компонент:
- Является абстракцией для всех компонентов, включая составные
- Объявляет интерфейс для объектов в композиция
Лист:
- Представляет листовые объекты в композиции
- Реализует все методы Component
Композит:
- Представляет составной компонент (компонент с дочерними элементами)
- Реализует методы манипулирования детьми
- Реализует все методы Component, обычно делегируя их своим детям
Пример кода для понимания Composite patter n:
import java.util.List; import java.util.ArrayList; interface Part{ public double getPrice(); public String getName(); } class Engine implements Part{ String name; double price; public Engine(String name,double price){ this.name = name; this.price = price; } public double getPrice(){ return price; } public String getName(){ return name; } } class Trunk implements Part{ String name; double price; public Trunk(String name,double price){ this.name = name; this.price = price; } public double getPrice(){ return price; } public String getName(){ return name; } } class Body implements Part{ String name; double price; public Body(String name,double price){ this.name = name; this.price = price; } public double getPrice(){ return price; } public String getName(){ return name; } } class Car implements Part{ List<Part> parts; String name; public Car(String name){ this.name = name; parts = new ArrayList<Part>(); } public void addPart(Part part){ parts.add(part); } public String getName(){ return name; } public String getPartNames(){ StringBuilder sb = new StringBuilder(); for ( Part part: parts){ sb.append(part.getName()).append(" "); } return sb.toString(); } public double getPrice(){ double price = 0; for ( Part part: parts){ price += part.getPrice(); } return price; } } public class CompositeDemo{ public static void main(String args[]){ Part engine = new Engine("DiselEngine",15000); Part trunk = new Trunk("Trunk",10000); Part body = new Body("Body",12000); Car car = new Car("Innova"); car.addPart(engine); car.addPart(trunk); car.addPart(body); double price = car.getPrice(); System.out.println("Car name:"+car.getName()); System.out.println("Car parts:"+car.getPartNames()); System.out.println("Car price:"+car.getPrice()); } }
выход:
Car name:Innova Car parts:DiselEngine Trunk Body Car price:37000.0
Объяснение:
- Часть - это лист
- Автомобиль содержит много частей
- В автомобиль добавлены запчасти автомобиля
- Цена Автомобиль = сумма (Цена каждой партии)
См. ниже вопрос о преимуществах и недостатках композиции и наследования.
Композиция точно так же звучит - вы создаете объект, вставляя части.
ИЗМЕНИТЬ, что остальная часть этого ответа ошибочно основана на следующем посылке. Это достигается с помощью интерфейсов. Например, используя вышеприведенный пример Car
,
Car implements iDrivable, iUsesFuel, iProtectsOccupants
Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
House implements iProtectsOccupants
Generator implements iUsesFuel
Итак, с помощью нескольких стандартных теоретических компонентов вы можете создать свой объект. Это ваша работа, чтобы заполнить, как House
защищает своих обитателей и как Car
защищает своих обитателей.
Наследование походит на все наоборот. Вы начинаете с полного (или полуполного) объекта, и вы заменяете или переопределяете различные биты, которые хотите изменить.
Например, MotorVehicle
может поставляться с методом Fuelable
и Drive
метод. Вы можете оставить метод Fuel так, как он есть, потому что это то же самое, что и заполнять мотоцикл и автомобиль, но вы можете переопределить метод Drive
, потому что Motorbike очень сильно отличается от Car
.
С наследованием некоторые классы уже полностью реализованы, а у других есть методы, которые вы вынуждены переопределять. С составом вам ничего не дано. (но вы можете реализовать интерфейсы, вызывая методы в других классах, если у вас что-то лежит).
Композиция считается более гибкой, потому что, если у вас есть такой метод, как iUsesFuel, вы можете иметь метод в другом месте (другой класс, другой проект), который просто беспокоится о том, чтобы иметь дело с объектами, которые могут подпитываться, независимо от того, это ли автомобиль, катер, плита, барбекю и т. д. Интерфейсы предусматривают, что классы, которые говорят, что они реализуют этот интерфейс, действительно имеют методы, которыми обладает этот интерфейс. Например,
iFuelable Interface:
void AddSomeFuel()
void UseSomeFuel()
int percentageFull()
, тогда у вас может быть метод где-то еще
private void FillHerUp(iFuelable : objectToFill) {
Do while (objectToFill.percentageFull() <= 100) {
objectToFill.AddSomeFuel();
}
Странный пример, но он показывает, что этот метод не заботится о том, что он заполняет, потому что объект реализует iUsesFuel
, он может быть заполнен. Конец истории.
Если вы использовали Inheritance вместо этого, вам понадобятся разные методы FillHerUp
для работы с MotorVehicles
и Barbecues
, если у вас не было какого-то довольно странного базового объекта ObjectThatUsesFuel, из которого унаследовать.
ThisCase
, а не в camelCase
. Поэтому лучше всего назвать ваши интерфейсы IDrivable
и т. Д. Возможно, вам не понадобится «I». если вы правильно перегруппируете все свои интерфейсы в пакет.
– ThePyroEagle
21 December 2015 в 11:15
Наследование выявляет отношение IS-A. Композиция выявляет связь HAS-A. Шаблон Statergy объясняет, что композиция должна использоваться в тех случаях, когда существуют семейства алгоритмов, определяющих конкретное поведение. Классический пример - тип утки, который реализует поведение мух.
public interface Flyable{
public void fly();
}
public class Duck {
Flyable fly;
public Duck(){
fly=new BackwardFlying();
}
}
Таким образом, мы можем иметь несколько классов которые реализуют полет, например:
public class BackwardFlying implements Flyable{
public void fly(){
Systemout.println("Flies backward ");
}
}
public class FastFlying implements Flyable{
public void fly(){
Systemout.println("Flies 100 miles/sec");
}
}
Если бы для наследования у нас было бы два разных класса птиц, которые снова и снова реализуют функцию мух. Это наследование и состав совершенно разные.
Композиция означает создание объекта для класса, который имеет отношение к этому конкретному классу. Предположим, что у Студента есть связь с Учётами;
Наследование - это предыдущий класс с расширенной функцией. Это означает, что этот новый класс - это класс Old с некоторой расширенной функцией. Предположим, что студент - студент, но все ученики - это люди. Так что есть отношения со студентами и людьми. Это Наследование.
В «Агрегации простого слова» означает «Отношения».
Состав - это особый случай агрегации. Более конкретно, ограниченная агрегация называется композицией. Когда объект содержит другой объект, если содержащийся объект не может существовать без существования контейнерного объекта, тогда он называется композицией. Пример: класс содержит студентов. Студент не может существовать без класса. Существует композиция между классом и учениками.
Зачем использовать Aggregation
Повторное использование кода
Когда использование агрегирования
Повторное использование кода также наилучшим образом достигается путем агрегирования, когда нет Относительный корабль
Наследование
Наследование является родительским отношением к дочерним отношениям, связанным с отношением. RelationShip
Наследование в Java - это механизм, в котором один объект приобретает все свойства и поведение родительского объекта.
Использование наследования в Java 1 Повторное использование кода. 2 Добавить дополнительную функцию в дочернем классе, а также переопределение метода (поэтому может быть достигнут полиморфизм выполнения).
Хотя оба наследования и композиции обеспечивают повторное использование кода, основное различие между композицией и наследованием в Java заключается в том, что Composition позволяет повторно использовать код без его расширения, но для Inheritance вы должны расширить класс для повторного использования кода или функциональности. Другое отличие от этого факта заключается в том, что с помощью композиции вы можете повторно использовать код для даже окончательного класса, который не является расширяемым, но Inheritance не может повторно использовать код в таких случаях. Кроме того, используя Composition, вы можете повторно использовать код из многих классов, поскольку они объявлены как только переменная-член, но с Inheritance вы можете повторно использовать форму кода только одного класса, потому что в Java вы можете расширять только один класс, поскольку множественное Наследование не поддерживается в Java , Вы можете сделать это на C ++, хотя из-за того, что один класс может расширять несколько классов. Кстати, вы всегда должны предпочитать композицию над наследованием в Java, ее не только я, но даже Джошуа Блох предложил в своей книге
Как наследование может быть опасным?
Давайте возьмем пример
public class X{
public void do(){
}
}
Public Class Y extends X{
public void work(){
do();
}
}
1) Как ясно в приведенном выше коде, класс Y имеет очень сильную связь с классом X. Если что-либо изменения в суперклассе X, Y могут резко нарушиться. Предположим, что в будущем класс X реализует работу метода с подписи
public int work(){
}
. Изменение выполняется в классе X, но это сделает класс Y несовместимым. Так что такая зависимость может подниматься до любого уровня, и это может быть опасно. Каждый раз, когда суперкласс может не иметь полной видимости для кода внутри всех его подклассов, и подкласс может постоянно замечать, что происходит в suerclass все время. Поэтому мы должны избегать этой сильной и ненужной связи.
Как композиция решает эту проблему?
Давайте посмотрим, пересматривая тот же пример
public class X{
public void do(){
}
}
Public Class Y{
X x=new X();
public void work(){
x.do();
}
}
Здесь мы создаем ссылку на класс X в классе Y и вызываем метод класса X, создавая экземпляр класса X. Теперь вся эта сильная связь исчезла. Суперклассы и подклассы теперь очень независимы друг от друга. Классы могут свободно вносить изменения, которые были опасны в ситуации наследования.
2) Второе очень хорошее преимущество композиции в том, что оно обеспечивает гибкость вызова метода. Например,
class X implements R
{}
class Y implements R
{}
public class Test{
R r;
}
В классе Test с использованием r reference Я могу вызывать методы класса X, а также класс Y. Эта гибкость никогда не существовала в наследовании
3) Еще одно большое преимущество: Unit testing
public class X{
public void do(){
}
}
Public Class Y{
X x=new X();
public void work(){
x.do();
}
}
В приведенном выше примере Если состояние экземпляра x неизвестно, его можно легко обмануть используя некоторые тестовые данные, и все методы могут быть легко протестированы. Это было невозможно вообще в наследовании. Поскольку вы сильно зависели от суперкласса, чтобы получить состояние экземпляра и выполнить любой метод.
4) Еще одна веская причина, по которой нам следует избегать наследования, заключается в том, что Java не поддерживает множественные наследование.
Давайте рассмотрим пример:
Public class Transaction {
Banking b;
public static void main(String a[])
{
b=new Deposit();
if(b.deposit()){
b=new Credit();
c.credit();
}
}
}
Полезно знать:
. Поэтому сделайте его привычкой всегда предпочитать состав над наследованием по разным причинам.
Нет, оба они разные. Композиция соответствует отношениям «HAS-A» и наследованию следуют отношения «IS-A». Лучшим примером для композиции был стратегический паттерн.
Я думаю, что этот пример ясно объясняет различия между наследованием и композицией.
В этом случае проблема решена с использованием наследования и композиции. Автор обращает внимание на то, что; в наследовании изменение в суперклассе может вызвать проблемы в производном классе, которые наследуют его.
Там вы также можете увидеть разницу в представлении, когда используете UML для наследования или композиции.
Состав состоит из того, что что-то состоит из отдельных частей и имеет прочные отношения с этими частями. Если основная часть умирает, так и другие, у них не может быть собственной жизни. Грубым примером является человеческое тело. Выньте сердце, и все остальные части отмирают.
Наследование - это то, где вы просто берете то, что уже существует и использует его. Нет сильных отношений. Человек мог наследовать свое отцовское имение, но он может обойтись без него.
Я не знаю Java, поэтому я не могу представить пример, но я могу дать объяснение понятий.
Наследование Vs Состав.
Наследования и состав используются для повторного использования и расширения поведения класса.
Наследование в основном используется в модели программирования алгоритмов семейства, такой как тип отношения IS-A, означает аналогичный объект. Пример.
. Они принадлежат семейству Car.
Композиция представляет собой отношение HAS-A Type.It показывает способность объекта, такого как Duster, иметь Five Gears, Safari имеет четыре Gears и т. д. Всякий раз, когда нам нужно расширить способность существующего класса, используйте композицию. Например, нам нужно добавить еще одна передача в объекте Duster, тогда мы должны создать еще один объект передачи и скомпоновать его объекту duster.
Мы не должны вносить изменения в базовый класс до тех пор, пока все производные классы не нуждаются в этих функциях. Для этого сценария мы должны использовать Composition.Such как
класс A Производится классом B
Класс A Производится классом C
Класс A Производится классом D.
Когда мы добавляем какие-либо функциональные возможности в класс А, тогда он доступен для всех подклассов, даже если классам C и D не требуются эти функции. Для этого сценария нам необходимо создать отдельный класс для этих функций и составить это к требуемому классу (вот класс B).
Ниже приведен пример:
// This is a base class
public abstract class Car
{
//Define prototype
public abstract void color();
public void Gear() {
Console.WriteLine("Car has a four Gear");
}
}
// Here is the use of inheritence
// This Desire class have four gears.
// But we need to add one more gear that is Neutral gear.
public class Desire : Car
{
Neutral obj = null;
public Desire()
{
// Here we are incorporating neutral gear(It is the use of composition).
// Now this class would have five gear.
obj = new Neutral();
obj.NeutralGear();
}
public override void color()
{
Console.WriteLine("This is a white color car");
}
}
// This Safari class have four gears and it is not required the neutral
// gear and hence we don't need to compose here.
public class Safari :Car{
public Safari()
{ }
public override void color()
{
Console.WriteLine("This is a red color car");
}
}
// This class represents the neutral gear and it would be used as a composition.
public class Neutral {
public void NeutralGear() {
Console.WriteLine("This is a Neutral Gear");
}
}
Inheritence означает повторное использование полной функциональности класса. Здесь мой класс должен использовать все методы суперкласса, и мой класс будет связан с суперклассом, и код будет дублироваться в обоих классах в случае наследования .
Но мы можем преодолеть все эти проблемы, когда мы используем композицию для общения с другим классом. композиция объявляет атрибут другого класса в мой класс, о котором мы хотим поговорить. и какую функциональность мы хотим от этого класса, которую мы можем получить, используя этот атрибут.