2016-03-30 4 views
3

Я уверен, что есть какое-то решение или шаблон, но у меня возникли проблемы с формулировкой вопроса, чтобы найти правильный ответ.Возврат объекта интерфейса из метода без экземпляра (неизвестного) конкретного типа

У меня есть интерфейс, ИКАР:

public interface ICar { 
void SetMake(String make); 
String GetMake(); 
} 

У меня есть класс, Автомобиль:

public class Car implements ICar { 
private String make; 

public void SetMake(String make) { this.make = make; } 

public String GetMake() { return make; } 

И у меня есть метод в другом классе, который не знает о машине. В духе полиморфизма я хотел бы этот метод, чтобы вернуть ИКАР, чтобы кто-то мог создать свою собственную реализацию ИКАР и использовать метод для своего автомобиля:

...//class preamble 
public ICar myMethod() { 
    ICar myCar = new ICar(); 
    myCar.SetMake("Ferrari"); 
    return myCar; 
} 

Я не уверен, почему что-то вроде этого не может работать. Поскольку ICar содержит метод SetMake, каждая реализация также должна содержать этот метод. Разумеется, jvm может каким-то образом помещать вызовы метода, а затем запускать их, когда доступна конкреция методов?

Еще одна идея - передать вновь созданную реализацию ICar в качестве параметра myMethod и вызвать его методы, но я чувствую, что это не может быть такой чистой реализацией, поскольку мне нужно будет создать экземпляр Car каждый время. Кроме того, если нет конструктора без параметров, мне пришлось бы создавать его с помощью фиктивных данных (также не чистых).

Я чувствую, что мне не хватает понимания, чтобы понять, как реализовать что-то подобное, и задавался вопросом, сможет ли кто-нибудь помочь?

Спасибо.

ответ

1

Один из способов сделать это, чтобы обеспечить тип класса в качестве параметра, а также сделать его родовое:

public <T extends ICar> T myMethod(Class<T> type) { 
    try { 
     T myCar = type.newInstance(); // T needs a visible no-args constructor 
     myCar.SetMake("Ferrari"); 
     return myCar; 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

(также отметить, что я имел дело с исключениями, обернув любой в RuntimeException)

Тогда вам нужно вызвать его так:

Car car = myMethod(Car.class); 

более гибкий способ ввести интерфейс фабрики и передать экземпляр фабрики для myMethod, который использует его для создания бетона ICar. Завод все еще может быть общим:

public interface ICarFactory<T extends ICar> { 
    T createCar(); 
} 

... 

public <T extends ICar> T myMethod(ICarFactory<T> factory) { 
    T myCar = factory.createCar(); 
    myCar.SetMake("Ferrari"); 
    return myCar; 
} 

... 

Car car = myMethod(carFactory); 
+1

Спасибо, мне нравится этот ответ, чтобы быть менее очевидным и демонстрируя реализацию. Думаю, я поеду на заводскую дорогу. – Tom

+0

@RicketyRick: добро пожаловать! –

0

Вам нужно сделать:

public ICar myMethod() { 
    ICar myCar = new Car(); 
    myCar.SetMake("Ferrari"); 
    return myCar; 
} 

вместо

public ICar myMethod() { 
    ICar myCar = new ICar(); 
    myCar.SetMake("Ferrari"); 
    myCar.return; 
} 

это утверждение: myCar.return; не имеет почти никакого смысла здесь ... и так ИКАР является интерфейсом вы не можете сделать ICar myCar = new ICar()

+0

'new ICar();' не будет компилироваться, так как это интерфейс. – starf

+0

Вы правы –

+0

Извините, я сделал ошибку с myCar.вернуть. Это не решает проблему, поскольку myMethod не может знать о типе автомобиля. – Tom

0

Вы не можете создать экземпляр интерфейса, поэтому new ICar() не является вариантом. Например, setMake должен установить переменную экземпляра, но интерфейс не имеет переменных экземпляра.

Однако, вы можете сделать что-то вроде:

 return new ICar() { 
     String m; 
     @Override 
     public void setMake(String make) 
     { 
      m = make; 
     } 



     @Override 
     public String getMake() 
     { 
      return m; 
     } 
    }; 

Таким образом, вы создаете объект, который реализует интерфейс.

Хотя этот ответ дает конкретный пример для ответа на вопрос (как это делает просто экземпляр объекта Car), более важный вопрос об интерфейсе заключается в том, что можно вернуть любой объект, реализуемый в интерфейсе. Существует несколько шаблонов проектирования, которые позволяют делегировать создание объектов. Например, можно рассмотреть вопрос о Factory Pattern.

+0

Может ли это быть «преобразовано» в тип автомобиля после возвращения? Поэтому я могу назвать myMethod следующим: 'Car newCar = myMethod();'? – Tom

+0

@RicketyRick Нет, метод возвращает 'interface', а не определенный класс. Таким образом, вызов будет «ICar newCar = myMethod()». Обычно лучше [Program to Interfaces, Not Implementations] (http://stackoverflow.com/questions/2697783/what-does-program-to-interfaces-not-implementations-mean), поэтому лучше использовать методы интерфейса, а не конкретную реализацию класса. – KevinO

+0

Есть ли способ сделать «Car newCar = myMethod();' без myMethod, который знает о конкретном типе автомобиля? (кроме передачи нового параметра Car в качестве параметра) – Tom

0

Вы не можете создать новый интерфейс, но можете его вернуть.

public ICar myMethod() { 
    ICar myCar = new Car(); 
    myCar.SetMake("Ferrari"); 
    return myCar; 
} 

интерфейс означает, что контракт это в основном говорят «Это ICar может быть Car, RedCar и тысячи различных реализаций, но вы не знаете, какой». Это смысл полиморфизма, который использует myMethod, не имеет понятия, какой автомобиль он получает, он может знать только contract, но не так, как они реализованы.

+0

В этом случае myMethod() не знает о классе Car. Я хотел сделать что-то вроде «Car myCar = myMethod()», где SetMake() вызывается на myCar без использования инъекции зависимостей. Это может быть даже невозможно, поэтому я спрашиваю. – Tom

+0

Вам не хватает точки интерфейса и полиморфизма. , если вы хотите использовать 'Car myCar = myMethod()', вы можете сделать это, изменив подпись, чтобы возвращать 'Car', а не' ICar ', НО позволяет сказать, что завтра вы решили изменить 'myMethod()' для создания нового 'RedCar', так что теперь вам нужно изменить подпись и тех, кто использует' myMethod', который может быть во многих местах. Используя возвращаемое значение 'ICar', вы решаете эту проблему (известную как инкапсуляция), если вы меняете' myMethod', чтобы создать «RedCar», весь ваш код остается тем же, потому что «RedCar реализует ICar» - это способ использования интерфейсов. –

0

Не совсем уверен, насколько я понимаю, что вы после этого точно, но я думаю, что вы действительно хотите наследовать свой Car от абстрактного базового класса, чтобы кто-то другой мог реализовать другие типы транспортных средств.

Что-то вроде этого:

public abstract class Vehicle { 
    public abstract void SetMake(String make); 
    public abstract String GetMake(); 
} 

public class Car extends Vehicle { 
    private String make; 
    public void SetMake(String make) { this.make = make; } 
    public String GetMake() { return make; } 
} 

public class Truck extends Vehicle { 
    private String make; 
    public void SetMake(String make) { this.make = make; } 
    public String GetMake() { return make; } 
} 



public Vehicle myMethod() { 
    Vehicle myCar = new Truck(); 
    myCar.SetMake("Ferrari"); 
    return myCar; 
}  
+0

Правда, можно было бы пройти эту иерархию, но я бы все же создал интерфейс IVehicle и вернул myMethod(). Этот пример также повторяет 'String make' в обоих подклассах, где он может обрабатываться в базе. Этот подход все еще оставляет одно программирование для конкретной реализации. – KevinO

0

Для метода myMethod() вы изложили

«В духе полиморфизма я хотел бы этот метод, чтобы вернуть ИКАР, чтобы кто-то мог бы создать их собственной реализации ICAR и использовать метод для их автомобиля: «

Что я понимаю из этого вы хотите сделать myMethod() Работа с различными конкретными типами ICar.

Объекты создаются при использовании new operator. Ваш существующий код не будет компилироваться, поскольку вы не можете создать экземпляр объекта интерфейсов. Чтобы исправить эту ошибку компиляции, я бы не хотел, чтобы вы создать конкретные объекты автомобиля:

ICar car = new Car(); // this will solve compile error but avoid doing this in your code 

Избегайте new оператора, пусть пользователь вашего кода использовать новый и впрыснуть конкретные реализации (вы можете даже не знать конкретные реализации, поскольку они зависят от пользователя, пользователи вашего кода).

Изменить метод, как показано ниже

...//class preamble 
public ICar myMethod(ICar myCar, String make) { 
    myCar.SetMake(make); 
    return myCar; 
} 

Я хотел бы предложить вам изучить: DIP principle и dependency injection, code to interface.

+0

Спасибо за ответ. Это единственный способ, которым я мог думать об этом, но было любопытно, есть ли способ сделать это без инъекции зависимости. – Tom

+0

@RicketyRick, я бы предложил вам сохранить код простым. [ссылка на принцип KISS]. Помните, что простота трудно достичь, если у вас есть это, тогда не пытайтесь ее сломать, иначе вы нарушите другие вещи. Также язык, точка зрения ООП: Интерфейсы АБСТРАКТЫ, как вы можете создать какой-либо объект из абстрактного или даже ожидать, что JVM сможет его создать. Реализация зависит от нашей потребности и кода, который мы вставляем в тело абстрактного метода во время реализации. Его либо вы создаете новые объекты типа Concrete, либо позволяете пользователям создавать их и вводить в ваш код. –

+0

JVM не может понять наши потребности в бизнесе, наши прецеденты, наши требования, onus на нас, чтобы обеспечить их реализацию. Мы не знаем о будущих требованиях, абстрактные методы дают нам возможность добавить реализацию в соответствии с нашими конкретными бизнес-потребностями позже, предоставив им реализацию. –

Смежные вопросы