Я хотел бы иметь возможность написать класс Java в одном пакете, который может обращаться к закрытым методам класса в другом пакете, не делая его подклассом другого класса. Это возможно?
Вот небольшой трюк, который я использую в JAVA для репликации механизма друзей C ++.
Допустим, у меня есть класс Romeo
и другой класс Juliet
. Они в разных пакетах (семейных) по причинам ненависти.
Romeo
хочет cuddle
Juliet
и Juliet
хочет только позволить Romeo
cuddle
ей.
В C ++, Juliet
объявил Romeo
бы (любовник), friend
но в Java нет таких вещей.
Вот классы и хитрость:
Дамы вперед :
package capulet;
import montague.Romeo;
public class Juliet {
public static void cuddle(Romeo.Love love) {
Objects.requireNonNull(love);
System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
}
}
Так что метод Juliet.cuddle
есть, public
но вам нужно Romeo.Love
вызвать его. Он использует это Romeo.Love
как «безопасность подписи», чтобы гарантировать, что только он Romeo
может вызвать этот метод, и проверит, что любовь реальна, так что среда выполнения выбросит, NullPointerException
если это так null
.
Теперь мальчики:
package montague;
import capulet.Juliet;
public class Romeo {
public static final class Love { private Love() {} }
private static final Love love = new Love();
public static void cuddleJuliet() {
Juliet.cuddle(love);
}
}
Класс Romeo.Love
является общедоступным, но его конструктор private
. Поэтому любой может увидеть это, но только Romeo
может построить это. Я использую статическую ссылку, поэтому то, Romeo.Love
что никогда не используется, создается только один раз и не влияет на оптимизацию.
Поэтому, Romeo
может , cuddle
Juliet
и только он может , потому что только он может построить и доступ к Romeo.Love
экземпляру, которая необходима Juliet
для cuddle
нее (или же она будет хлопнуть вас с NullPointerException
).
Romeo
этоLove
дляJulia
вечного, изменивlove
поле наfinal
;-).Разработчики Java явно отвергли идею друга, как это работает в C ++. Вы кладете своих «друзей» в один пакет. Частная, защищенная и пакетная защита обеспечивается как часть языкового дизайна.
Джеймс Гослинг хотел, чтобы Java была C ++ без ошибок. Я считаю, что он чувствовал, что этот друг был ошибкой, потому что он нарушает принципы ООП. Пакеты обеспечивают разумный способ организации компонентов, не слишком заботясь об ООП.
Н.Р. указал, что вы можете обмануть с помощью отражения, но даже это работает, только если вы не используете SecurityManager. Если вы включите стандартную безопасность Java, вы не сможете обмануть с помощью рефлексии, если не напишите политику безопасности, которая специально разрешит это.
источник
friend
нарушил ООП (в частности, больше, чем доступ к пакетам), то он действительно не понимал этого (вполне возможно, многие люди неправильно это понимают).Концепция «друга» полезна в Java, например, для отделения API от его реализации. Для классов реализации обычно требуется доступ к внутренним компонентам класса API, но они не должны быть доступны клиентам API. Это может быть достигнуто с помощью шаблона «Friend Accessor», как описано ниже:
Класс, предоставляемый через API:
Класс, обеспечивающий функциональность «друг»:
Пример доступа из класса в пакете реализации 'friend':
источник
Есть два решения для вашего вопроса, которые не включают хранение всех классов в одном пакете.
Во-первых, использовать шаблон Friend Accessor / Friend Package , описанный в (Практическое проектирование API, Tulach 2008).
Второе - использовать OSGi. Существует статья здесь объяснить , как OSGi решает эту задачу.
Смежные вопросы: 1 , 2 и 3 .
источник
Насколько я знаю, это невозможно.
Может быть, вы могли бы дать нам более подробную информацию о вашем дизайне. Подобные вопросы, вероятно, являются результатом недостатков дизайна.
Просто подумай
источник
Ответ Эйрикмы прост и превосходен. Я мог бы добавить еще одну вещь: вместо общедоступного метода getFriend (), чтобы получить друга, которого нельзя использовать, вы можете пойти еще дальше и запретить получение друга без токена: getFriend (Service.FriendToken). Этот FriendToken будет внутренним открытым классом с закрытым конструктором, так что только Service может его создать.
источник
Вот ясный пример использования с повторно используемым
Friend
классом. Преимущество этого механизма заключается в простоте использования. Может быть, хорошо для того, чтобы дать классам модульного теста больший доступ, чем остальной части приложения.Для начала, вот пример того, как использовать
Friend
класс.Тогда в другом пакете вы можете сделать это:
Friend
Класс следующим образом .Однако проблема в том, что им можно злоупотреблять так:
Теперь, возможно, верно, что у
Other
класса нет открытых конструкторов, поэтому приведенный вышеAbuser
код невозможен. Однако, если ваш класс делает общедоступный конструктор , то это, вероятно , целесообразно дублировать класс друга как внутренний класс. Возьмите этотOther2
класс в качестве примера:И тогда
Owner2
класс будет таким:Обратите внимание, что у
Other2.Friend
класса есть приватный конструктор, что делает этот способ более безопасным.источник
Предоставленное решение было, возможно, не самым простым. Другой подход основан на той же идее, что и в C ++: закрытые члены не доступны вне пакета / частной области, за исключением определенного класса, который владелец делает своим другом.
Класс, которому нужен доступ друга к члену, должен создать внутренний публичный абстрактный «класс друга», в который класс, владеющий скрытыми свойствами, может экспортировать доступ, возвращая подкласс, который реализует методы реализации доступа. Метод «API» класса-друга может быть закрытым, поэтому он недоступен за пределами класса, которому требуется доступ-друг. Его единственное утверждение - это вызов абстрактного защищенного члена, который реализует экспортирующий класс.
Вот код:
Сначала тест, который проверяет, что это действительно работает:
Затем Сервис, которому нужен доступ друга к пакетному приватному члену Entity:
Наконец, класс Entity, обеспечивающий дружественный доступ к закрытому члену пакета только для класса application.service.Service.
Хорошо, я должен признать, что это немного дольше, чем "friend service :: Service;" но может быть возможно сократить его, сохранив проверку во время компиляции с использованием аннотаций.
источник
В Java возможно иметь «дружеское отношение к пакету». Это может быть полезно для модульного тестирования. Если вы не укажете private / public / protected перед методом, он будет "другом в пакете". Класс в том же пакете сможет получить к нему доступ, но он будет закрытым вне класса.
Это правило не всегда известно, и оно является хорошим приближением к ключевому слову «Друг» в C ++. Я считаю это хорошей заменой.
источник
Я думаю, что классы друзей в C ++ похожи на концепцию внутреннего класса в Java. Используя внутренние классы, вы можете определить класс, включающий в себя класс. Закрытый класс имеет полный доступ к открытым и закрытым членам включающего его класса. см. следующую ссылку: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
источник
Я думаю, что подход к использованию паттерна доступа друга слишком сложен. Мне пришлось столкнуться с той же проблемой, и я решил использовать старый добрый конструктор копирования, известный из C ++, в Java:
В вашем приложении вы можете написать следующий код:
Преимущество этого метода в том, что только ваше приложение имеет доступ к защищенным данным. Это не совсем замена ключевого слова друга. Но я думаю, что это очень удобно, когда вы пишете пользовательские библиотеки и вам нужен доступ к защищенным данным.
Всякий раз, когда вам приходится иметь дело с экземплярами ProtectedContainer, вы можете обернуть ваш ProtectedAccessor вокруг него, и вы получите доступ.
Он также работает с защищенными методами. Вы определяете их защищенными в вашем API. Позже в вашем приложении вы пишете закрытый класс-обертку и выставляете защищенный метод как открытый. Вот и все.
источник
ProtectedContainer
может быть подкласс за пределами пакета!Если вы хотите получить доступ к защищенным методам, вы можете создать подкласс класса, который вы хотите использовать, который предоставляет методы, которые вы хотите использовать как общедоступные (или внутренние для пространства имен, чтобы быть более безопасными), и иметь экземпляр этого класса в своем классе. (используйте его как прокси).
Что касается частных методов (я думаю), вам не повезло.
источник
Я согласен с тем, что в большинстве случаев ключевое слово Friend не требуется.
И, наконец, если это действительно необходимо, в других ответах упоминается схема доступа к друзьям.
источник
Не используя ключевое слово или около того.
Вы можете «обмануть», используя рефлексию и т. Д., Но я бы не рекомендовал «обманывать».
источник
Метод, который я нашел для решения этой проблемы, заключается в создании объекта доступа, например, так:
Первый код, который называется
getAccessor()
«утверждает право собственности» на метод доступа. Обычно это код, который создает объект.Это также имеет преимущество перед механизмом друга C ++, поскольку позволяет ограничивать доступ на уровне экземпляра , а не на уровне класса . Управляя ссылкой доступа, вы контролируете доступ к объекту. Вы также можете создавать несколько средств доступа и предоставлять каждому доступ по-разному, что позволяет детально контролировать, какой код может получить доступ к чему:
Наконец, если вы хотите, чтобы вещи были немного более организованными, вы можете создать эталонный объект, который содержит все вместе. Это позволяет запрашивать все средства доступа одним вызовом метода, а также хранить их вместе со связанным экземпляром. Получив ссылку, вы можете передать методы доступа к нужному коду:
После долгих ударов головой (не очень), это было мое окончательное решение, и мне оно очень нравится. Он гибкий, простой в использовании и позволяет очень хорошо контролировать доступ к классам. ( Доступ только со ссылкой очень полезен.) Если вы используете для доступа / ссылок защищенный вместо частного, подклассы Foo могут даже возвращать расширенные ссылки из
getReference
. Он также не требует отражения, поэтому его можно использовать в любой среде.источник
Начиная с Java 9, модули могут использоваться для того, чтобы во многих случаях это не возникало.
источник
Я предпочитаю делегирование или композицию или фабричный класс (в зависимости от проблемы, которая приводит к этой проблеме), чтобы не делать его публичным классом.
Если это проблема «интерфейсов / классов реализации в разных пакетах», то я бы использовал общедоступный класс фабрики, который был бы в том же пакете, что и пакет impl, и предотвратил бы раскрытие класса impl.
Если возникает проблема «я не хочу делать этот класс / метод общедоступным только для того, чтобы обеспечить эту функциональность для какого-то другого класса в другом пакете», то я бы использовал открытый класс делегата в том же пакете и выставил бы только эту часть функциональности. нужен "чужому" классу.
Некоторые из этих решений основаны на архитектуре загрузки классов целевого сервера (комплект OSGi, WAR / EAR и т. Д.), Соглашениях о развертывании и именовании пакетов. Например, предложенное выше решение, паттерн «Friend Accessor» является умным для обычных Java-приложений. Интересно, сложно ли реализовать его в OSGi из-за различий в стиле загрузки классов?
источник
Я не знаю, полезно ли это кому-либо, но я справился с этим следующим образом:
Я создал интерфейс (AdminRights).
Каждый класс, который должен иметь возможность вызывать указанные функции, должен реализовывать AdminRights.
Затем я создал функцию HasAdminRights следующим образом:
источник
Однажды я увидел решение, основанное на отражении, которое выполняло «проверку друга» во время выполнения, используя отражение и проверку стека вызовов, чтобы узнать, разрешено ли это делать классу, вызывающему метод. Будучи проверкой во время выполнения, она имеет очевидный недостаток.
источник