Интерфейсы в Java: нельзя сделать реализованные методы защищенными или частными

Я знаю, что интерфейс должен быть публичным. Однако я этого не хочу.

Я хочу, чтобы мои реализованные методы были доступны только из их собственного пакета, поэтому я хочу, чтобы мои реализованные методы были защищены.

Проблема в том, что я не могу защитить интерфейс или реализованные методы.

Что такое работа? Есть ли шаблон проектирования, относящийся к этой проблеме?

Согласно руководству по Java, абстрактный класс тоже не справился бы с этой задачей.


person jbu    schedule 08.01.2009    source источник
comment
Что не так с абстрактными классами (кроме создания подкласса?)   -  person OscarRyz    schedule 08.01.2009
comment
Если абстрактный класс содержит только объявления абстрактных методов, вместо этого он должен быть объявлен как интерфейс. Я не подвергал сомнению причину этого, но думаю, что должен.   -  person jbu    schedule 08.01.2009
comment
Руководство по Java направляет вас, давая хорошие советы. Это не правило.   -  person Rob Kennedy    schedule 08.01.2009
comment
В большинстве случаев да, но это один из тех случаев, когда вам не обязательно   -  person OscarRyz    schedule 08.01.2009
comment
Это не обязательно, чтобы интерфейс был общедоступным. Кто-нибудь любезно, отредактируйте вопрос, чтобы прояснить ситуацию. Спасибо.   -  person Adeel Ansari    schedule 08.01.2009
comment
Если вы не хотите раскрывать реализацию своего интерфейса другим классам, создайте частный внутренний класс, который реализует рассматриваемый интерфейс.   -  person pag3faul7    schedule 08.09.2017


Ответы (12)


прочтите это.

"Спецификатор общего доступа указывает, что интерфейс может использоваться любым классом в любом пакете. Если вы не укажете, что интерфейс является общедоступным, ваш интерфейс будет доступен только для классов, определенных в том же пакете, что и интерфейс < / strong>. "

Это то, что вы хотите?

person duffymo    schedule 08.01.2009
comment
Я думаю, есть небольшая путаница в вопросе между видимостью самого интерфейса и видимостью методов интерфейса. Вы можете сделать интерфейс закрытым, но методы всегда будут общедоступными. - person Jon Skeet; 08.01.2009
comment
@Jon: В чем причина / идея методы всегда будут общедоступными? - person M-D; 25.08.2013
comment
@ M-D: Честно говоря, не знаю. - person Jon Skeet; 25.08.2013
comment
@MD, для этого нет особой причины, скорее всего, просто так решено (stackoverflow.com/a/5377300/632951), точно так же, как как произвольно исключили статические абстрактные данные. - person Pacerier; 10.09.2014

Вы, класс, можете использовать защиту пакетов и по-прежнему реализовывать интерфейс:

class Foo implements Runnable 
{
    public void run()
    {
    }
}

Если вы хотите, чтобы некоторые методы были защищены / упакованы, а другие нет, похоже, что ваши классы имеют более одной ответственности и должны быть разделены на несколько.

Отредактируйте после прочтения комментариев к этому и другим ответам:

Если вы почему-то думаете, что видимость метода влияет на возможность его вызова, подумайте еще раз. Не впадая в крайности, вы не можете помешать кому-либо использовать отражение для идентификации методов вашего класса и их вызова. Однако это не проблема: если кто-то не пытается взломать ваш код, он не будет вызывать случайные методы.

Вместо этого думайте о частных / защищенных методах как об определении контракта для подклассов и используйте интерфейсы для определения контракта с внешним миром.

Да, и тому, кто решил, что мой пример следует использовать распорки K&R: если это указано в Условиях обслуживания, конечно. В противном случае, разве вы не можете найти что-нибудь лучше, чем заняться со своим временем?

person kdgregory    schedule 08.01.2009
comment
Хорошая точка зрения! +1 пока интерфейс является общедоступным, реализация может быть модификатором доступа по умолчанию. Мне кажется, это правильный ответ! :) - person OscarRyz; 08.01.2009
comment
Я не понимаю этого ответа. Поскольку run общедоступен, его можно вызывать из любого пакета, верно? Я не хочу, чтобы мой реализованный метод вызывался из других пакетов. - person jbu; 08.01.2009
comment
но класс, из которого вы его вызываете, виден только в пакете. вы вызываете run на конкретном экземпляре, который не является общедоступным. - person duffymo; 08.01.2009
comment
О, я вижу. Да, думаю, это то, что я ищу. Спасибо, парни. - person jbu; 08.01.2009
comment
Хм, это действительно небезопасно. Если ваш пакет позволяет Foo ускользать, любой может ссылаться на него как на Runnable и вызывать метод run. - person erickson; 08.01.2009
comment
истина, но если позволить объектам ускользнуть - проблема с вашим приложением, вам необходимо пересмотреть дизайн - person kdgregory; 08.01.2009
comment
@jbu - если это действительно то, что вы ищете, вы должны принять его ... - person ericsoco; 22.12.2011
comment
@kdgregory Мне нравится твоя последняя часть этого поста (я имею в виду это со всей искренностью). Я терпеть не могу людей, которые слишком рьяны в отношении своих правок и модерации. - person Matthew; 25.04.2020

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

Обычно это потому, что у меня есть класс с конкретным общедоступным API, который должен реализовывать что-то еще, чтобы выполнить свою работу (довольно часто потому, что что-то еще было обратным вызовом, замаскированным под интерфейс ‹grin›) - это часто случается с такими вещами, как Comparable. Я не хочу, чтобы публичный API был загрязнен (принудительно публичной) реализацией интерфейса.

Надеюсь это поможет.

Кроме того, если вы действительно хотите, чтобы методы были доступны только для пакета, вам не нужен описатель защищенной области, вам нужен описатель области по умолчанию (опущен). Использование protected, конечно же, позволит подклассам видеть методы.

Кстати, я думаю, что причина, по которой методы интерфейса считаются общедоступными, заключается в том, что в значительной степени исключение имеет интерфейс, который реализуется только классами в том же пакете; они очень часто вызываются чем-то в другом пакете, что означает, что они должны быть общедоступными.

person Lawrence Dol    schedule 08.01.2009

Этот вопрос основан на неверном утверждении:

Я знаю, что интерфейс должен быть общедоступным

Не совсем, у вас могут быть интерфейсы с модификатором доступа по умолчанию.

Проблема в том, что я не могу защитить интерфейс или реализованные методы

Вот:

C:\oreyes\cosas\java\interfaces>type a\*.java
a\Inter.java
package a;

interface Inter {
    public void face();
}

a\Face.java
package a;

class Face implements Inter {
    public void face() {
        System.out.println( "face" );
    }
}


C:\oreyes\cosas\java\interfaces>type b\*.java
b\Test.java

package b;
import a.Inter;
import a.Face;

public class Test {
    public static void main( String [] args ) {
        Inter inter = new Face();
        inter.face();
    }
}

C:\oreyes\cosas\java\interfaces>javac -d . a\*.java b\Test.java
b\Test.java:2: a.Inter is not public in a; cannot be accessed from outside package
import a.Inter;
        ^
b\Test.java:3: a.Face is not public in a; cannot be accessed from outside package
import a.Face;
        ^
b\Test.java:7: cannot find symbol
symbol  : class Inter
location: class b.Test
        Inter inter = new Face();
        ^
b\Test.java:7: cannot find symbol
symbol  : class Face
location: class b.Test
        Inter inter = new Face();
                          ^
4 errors

C:\oreyes\cosas\java\interfaces>

Следовательно, достигнув желаемого, предотвратите использование интерфейса и класса за пределами пакета.

person OscarRyz    schedule 13.01.2009
comment
Вкл., У вас могут быть интерфейсы с модификатором доступа по умолчанию ... Нет, все методы интерфейса являются общедоступными; фактически, определение области видимости не имеет значения и игнорируется. - person Lawrence Dol; 05.06.2015
comment
@LawrenceDol И я никогда не говорил иначе, в этом примере даже метод явно отмечен как общедоступный, но интерфейс не является общедоступным, поэтому к его методу нельзя получить доступ к пакету, что делает их эффективно защищенными. - person OscarRyz; 08.06.2015
comment
Ах, извините, я неправильно понял то, что вы написали. Я ошибочно предположил, что у вас может быть интерфейс [методы] с модификатором доступа по умолчанию. - person Lawrence Dol; 10.06.2015

Вот как это можно сделать с помощью абстрактных классов.

Единственное неудобство - это то, что он делает вас «подклассом».

Согласно руководству по java, вы должны следовать этому совету "в большинстве" случаев, но я думаю, что в этой ситуации все будет нормально.

public abstract class Ab { 
    protected abstract void method();
    abstract void otherMethod();
    public static void main( String [] args ) { 
        Ab a = new AbImpl();
        a.method();
        a.otherMethod();
    }
}
class AbImpl extends Ab {
    protected void method(){
        System.out.println( "method invoked from: " + this.getClass().getName() );
    }
    void otherMethod(){ 
        System.out.println("This time \"default\" access from: " + this.getClass().getName()  );
    }
}
person OscarRyz    schedule 08.01.2009

Вот еще одно решение, вдохновленное идиомой C ++ Pimpl.

Если вы хотите реализовать интерфейс, но не хотите, чтобы эта реализация была общедоступной, вы можете создать составной объект анонимного внутреннего класса, который реализует интерфейс.

Вот пример. Допустим, у вас есть такой интерфейс:

public interface Iface {
    public void doSomething();
}

Вы создаете объект типа Iface и помещаете туда свою реализацию:

public class IfaceUser {
    private int someValue;
    // Here's our implementor
    private Iface impl = new Iface() {
        public void doSomething() {
            someValue++;
        }
    };
}

Всякий раз, когда вам нужно вызвать doSomething(), вы вызываете его на своем составном объекте impl.

person Karl Giesing    schedule 24.06.2014
comment
Мне нравится это решение, хотя я думаю, что оно делает код беспорядочным, оно решает проблему наиболее гибким способом. - person sparkonhdfs; 15.06.2016

Я только что наткнулся на это, пытаясь создать защищенный метод с намерением использовать его только в тестовом примере. Я хотел удалить тестовые данные, которые я поместил в таблицу БД. В любом случае меня вдохновил @Karl Giesing / a / 24395109/1024005 "> post. К сожалению, это не сработало. Я нашел способ заставить его работать, используя защищенный внутренний класс.

Интерфейс:

package foo;
interface SomeProtectedFoo {
    int doSomeFoo();
}

Затем внутренний класс определяется как защищенный в общедоступном классе:

package foo;
public class MyFoo implements SomePublicFoo {
    // public stuff
    protected class ProtectedFoo implements SomeProtectedFoo {
        public int doSomeFoo() { ... }
    }
    protected ProtectedFoo pFoo;
    protected ProtectedFoo gimmeFoo() {
        return new ProtectedFoo();
    }
}

Затем вы можете получить доступ к защищенному методу только из других классов в том же пакете, как показано в моем тестовом коде:

package foo;
public class FooTest {
    MyFoo myFoo = new MyFoo();
    void doProtectedFoo() {
        myFoo.pFoo = myFoo.gimmeFoo();
        myFoo.pFoo.doSomeFoo();
    }
}

Немного поздно для оригинального плаката, но эй, я только что его нашел. : D

person siliconsmiley    schedule 02.12.2014
comment
Да, извините, мой пост был просто дать общее представление, а не представить полнофункциональный код. Но эй, по крайней мере, я кого-то вдохновил. :) - person Karl Giesing; 13.02.2016
comment
Моя ярость к ботанику сильна, когда я не могу скопировать код из stackoverflow. : D - person siliconsmiley; 19.02.2016

Вы можете использовать инкапсуляцию вместо наследования.

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

Тогда вы сможете выставлять только то, что хотите.

Очевидным недостатком этого является то, что вы должны явно передавать методы для всего, что вы хотите раскрыть. И это не будет подкласс ...

person Michael Haren    schedule 08.01.2009

Я бы просто создал абстрактный класс. В этом нет ничего плохого.

person Hash    schedule 08.01.2009
comment
Как ты можешь говорить так?! абстрактные классы - это корень всех зол! : D - person Ritwik Bose; 19.07.2010

С помощью интерфейса вы хотите определить методы, которые могут быть предоставлены множеством реализующих классов. Наличие интерфейса с защищенными методами просто не служит этой цели.

Я предполагаю, что вашу проблему можно решить, изменив иерархию классов.

person Jonas Eicher    schedule 12.12.2012

Один из способов обойти это (в зависимости от ситуации) - просто создать анонимный внутренний класс, реализующий интерфейс с областью видимости protected или private. Например:

public class Foo {
    interface Callback {
        void hiddenMethod();
    }

    public Foo(Callback callback) {
    }
}

Затем в пользователе Foo:

public class Bar {
    private Foo.Callback callback = new Foo.Callback() { 
        @Override public void hiddenMethod() { ... }
    };
    private Foo foo = new Foo(callback);
}

Это избавит вас от следующего:

public class Bar implements Foo.Callback {
    private Foo foo = new Foo(this);

    // uh-oh! the method is public!
    @Override public void hiddenMethod() { ... }
}
person Jin    schedule 12.04.2016

Думаю, теперь вы можете использовать его с выпуском Java 9. Из заметок openJdk для Java 9,

Поддержка частных методов в интерфейсах кратко рассматривалась для включения в Java SE 8 как часть усилий по добавлению поддержки лямбда-выражений, но была отменена, чтобы лучше сосредоточиться на задачах с более высоким приоритетом для Java SE 8. Теперь предлагается, чтобы поддержка для методов частного интерфейса, тем самым позволяя неабстрактным методам интерфейса совместно использовать код между собой.

см. https://bugs.openjdk.java.net/browse/JDK-8071453

person Mounika Sai    schedule 21.09.2017