Где я должен использовать интерфейс как тип переменной? Я вижу, что многие люди говорят, что это лучше всего, но каждый пример относится к коллекциям:
List<String> list = new ArrayList<>()
Применяется ли эта практика в основном к коллекциям? Я никогда не видел, чтобы кто-нибудь использовал интерфейс, например, при объявлении потоков ввода-вывода.
Для Java 10 и более поздних версий рассмотрите возможность использования var для локальных переменных.
См. Что значит «программировать интерфейс»?
См. Это stackoverflow.com/questions/6643136/…




Идея в том, что вы хотите скрыть реальную реализацию, если она не важна. Обычно это упрощает код и позволяет избежать случайного использования специфической особенности реализации.
Поэтому используйте только ArrayList с new. Используйте List везде. (Если у вас нет на то веской причины)
Примечание. Для Java 10 вы можете использовать синтаксис var для локальных переменных:
var list = new ArrayList<String>();
Даже без алмаза <>?
@GhostCat да.
@GhostCat да - тогда используется Object. В этом случае необходима строка. Спасибо, что указали на это.
Сказать здесь спасибо ... поскольку ваш ответ вдохновил на этот новый вопрос ;-)
Использование интерфейсов лучше, чем конкретные классы, потому что это проще, если вы хотите переключить реализацию. Но это применимо только в том случае, если у вас есть выгода от этого; использование List вместо ArrayList в методе из 5 строк не сильно меняет ваш код. Преимущество больше, когда вы получаете их как ссылку на метод или конструктор, потому что вы используете интерфейс / контракт, и «пользователь» вашего кода (это может быть вы) может легко передать другую реализацию для улучшения программы или как имитацию в тест.
Для ввода-вывода довольно часто работают с InputStream и OutpuStream, конкретный тип - на усмотрение пользователя. Так что вы можете использовать InputStream для чтения либо из сети, либо из локального файла.
Рекомендуется ссылаться на интерфейс, потому что таким образом вы всегда можете изменить фактическую реализацию без каких-либо изменений в вызывающей стороне этого интерфейса. Предположим, у вас есть среда CDI, такая как Spring. У вас может быть ServiceInterface с несколькими реализациями, но контроллер никогда не увидит фактическую реализацию.
Контроллер:
@Controller
public class MyController {
@Autowired
private ServiceInterface service;
/*stuff*/
}
ServiceInterface:
public interface ServiceInterface{ /*stuff*/ }
Реализация «А»:
@Service
@Profile("A")
@Primary
public class ServiceImplementationA implements ServiceInterface {
/*stuff*/
}
Реализация «Б»:
@Service
@Profile("B")
public class ServiceImplementationB implements ServiceInterface {
/*stuff*/
}
Spring имеет свой собственный механизм для выбора реализаций (которые называются профилями), но существуют альтернативы, подобные фабрикам. Пока интерфейс не меняется, вы можете переключаться между двумя реализациями службы: класс контроллера не обнаружит никаких изменений.
Это своего рода дальнозоркость. Да, есть правило,
Always code to interface.
Но почему?
Вы используете new ArrayList<>() для типа определения и List<String> для декларативного типа. Это хорошая практика. Теперь давайте подумаем, как правило, для nonce производительность ArrayList действительно подходит для ваших данных и программы. Что делать, если существует новый тип контейнера, например называется NicePerformedList, который также реализует интерфейс List и значительно улучшает производительность вашей программы через 5 лет. Будете ли вы проводить рефакторинг всех декларативных типов вместо простого изменения типа определения?
Когда существуют разные реализации методов между разными классами, рекомендуется иметь интерфейс с сигнатурой этих методов и реализовывать их в классах. Таким образом, независимо от того, какая реализация используется, мы можем использовать интерфейс как переменную или передать его другим методам как внутреннюю переменную.
interface MathInterface{
int sum();
}
class A implements MathInterface
{
@Override
int sum()
{
//implementation
}
}
class B implements MathInterface
{
@Override
int sum()
{
//implementation
}
}
class Main
{
MathInterface mathInterface=new B();
doSomeThing(mathInterface);
}
Если у вас есть несколько реализаций интерфейса и вы хотите быстро подключать и играть, а также другую реализацию, то изменение ограничивается только этапом создания экземпляра и больше нигде.