Интерфейсы в Java: нельзя сделать реализованные методы защищенными или приватными - PullRequest
27 голосов
/ 08 января 2009

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

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

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

Что такое обходной путь? Есть ли шаблон дизайна, который относится к этой проблеме?

Из руководства по Java абстрактный класс тоже не справится с работой.

Ответы [ 12 ]

24 голосов
/ 08 января 2009

читать это .

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

Ты этого хочешь?

11 голосов
/ 08 января 2009

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

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

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

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

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

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

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

8 голосов
/ 08 января 2009

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

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

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

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

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

4 голосов
/ 13 января 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>

Следовательно, достигая того, что вы хотели, предотвратите использование интерфейса и класса вне пакета.

3 голосов
/ 08 января 2009

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

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

Согласно руководству по 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()  );
    }
}
1 голос
/ 03 декабря 2014

Я только что натолкнулся на это, пытаясь создать защищенный метод с намерением использовать его только в тестовом примере. Я хотел удалить тестовые данные, которые я вставил в таблицу БД. В любом случае меня вдохновил @ Karl Giesing 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

1 голос
/ 24 июня 2014

Вот еще одно решение, основанное на идиоме 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 объекта.

0 голосов
/ 21 сентября 2017

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

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

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

0 голосов
/ 13 апреля 2016

Один из способов обойти это - (в зависимости от ситуации) просто создать анонимный внутренний класс, реализующий интерфейс с областью действия 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() { ... }
}
0 голосов
/ 12 декабря 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...