Friend class in java

skuzzle / friend-classes.md

API designers might know the struggle: you have a package defining the public API as interfaces and then you have another package containing the implementation. How do you make the implementation accessible to the outside but also restrict unwanted instantiations?

Forbidding unwanted usage of your API actually makes it easier to use because it is harder to be used the wrong way.

Let’s look at this simple example and start with a public service interface:

package com.yourdomain.api; public interface PublicService < // .. Service methods >

Then, within the internal package, the service’s implementation:

package com.yourdomain.internal; import com.yourdomain.api.PublicService; public class PublicServiceImpl implements PublicService < // . Service method implementations >

You do not want client code to directly instantiate the PublicServiceImpl class, because it should not depend on the actual implementation. One way to do so is putting a factory class with static methods into the api package like:

package com.yourdomain.api; import com.yourdomain.internal.PublicServiceImpl; public class PublicServiceFactory < public static PublicService create() < return new PublicServiceImpl(); > >

Now, using the factory, client code does not need to know about the existence of the PublicServiceImpl . The problem is, this approach does not enforce the usage of the factory. Clients could still go and call the implementation’s constructor because it needs to be public in order for the factory to see it.

My proposal for solving this issue, is to use an instance of the factory class as guard for instantiating the PublicServiceImpl . To do so, we introduce a constructor argument within our implementation:

package com.yourdomain.internal; import com.yourdomain.api.PublicService; import com.yourdomain.api.PublicServiceFactory; public class PublicServiceImpl implements PublicService < // an instance of the Factory must be passed in order to construct an instance of the // service public PublicServiceImpl(PublicServiceFactory guard) < if (guard == null) < throw new IllegalArgumentException("no guard supplied"); > > // . Service method implementations >

Now, to instantiate the service, you first need an instance of its factory. The next step is to prevent the factory class from being instantiated from the outside and to pass an instance of itself when constructing the service instance.

package com.yourdomain.api; import com.yourdomain.internal.PublicServiceImpl; // make class final to prevent inheritance public final class PublicServiceFactory < private PublicServiceFactory() < // hide constructor from others > public static PublicService create() < // b/c of the private constructor we are the only one who can create the guard // instance final PublicServiceFactory guard = new PublicServiceFactory(); return new PublicServiceImpl(guard); > >

This way we enforce that the factory is the only one place where our service can get instantiated and clients must commit themself to a special malicious intent in order to create an instance of the implementation themself.

Note that this approach might not be suitable in every case. One disadvantage is that you introduce a package cycle between the internal and the api package. This prevents you from separating them into different artifacts.

Читайте также:  Java memory for object

In guice-async-extension I’m using this technique to pass a guice Module from the internal- to the api package. This also allows to make all the service implementations package private because they will be instantiated by guice and thus the only public class within the internal package is the module for configuring the guice bindings.

Источник

Friend Class in Java

Friend Class in Java

Friend class is the functionality of C++, which is used to access the non-public members of a class. Java doesn’t support the friend keyword, but we can achieve the functionality.

This tutorial demonstrates how to create a friend class in Java.

Friend Class in Java

The friend concept can also be implemented in Java. For example, two colleagues from different departments of a company.

Both colleagues don’t know each other, but they need to cooperate for some work. Let’s set one employee as Jack and the other as Michelle based on a friend pattern.

We need to create two packages and implement both classes to implement this example.

The class Jack in Department(package) Delftstack1 :

package Delftstack1;  import Delftstack2.Michelle;  public final class Jack   static   // Declare classes in the Delftstack2 package as 'friends'  Michelle.setInstance(new Michelle_Implement());  >   // Constructor is Only accessible by 'friend' classes.  Jack()    >   // This Method is Only accessible by 'friend' classes.  void HelloDelftstack()   System.out.println("Hello! I am Jack from Delftstack");  >   static final class Michelle_Implement extends Michelle   protected Jack createJack()   return new Jack();  >   protected void sayHello(Jack jack)   jack.HelloDelftstack();  >  > > 

The class Michelle in Department(package) Delftstack2 :

package Delftstack2;  import Delftstack1.Jack;  public abstract class Michelle    private static Michelle instance;   static Michelle getInstance()   Michelle a = instance;  if (a != null)   return a;  >   return createInstance();  >   private static Michelle createInstance()   try   Class.forName(Jack.class.getName(), true,  Jack.class.getClassLoader());  > catch (ClassNotFoundException e)   throw new IllegalStateException(e);  >   return instance;  >   public static void setInstance(Michelle michelle)   if (instance != null)   throw new IllegalStateException("Michelle instance already set");  >   instance = michelle;  >   protected abstract Jack createJack();   protected abstract void sayHello(Jack jack); > 

The class to implement the main method:

package Delftstack2;  import Delftstack1.Jack;  public final class Friend_Class   public static void main(String[] args)   Michelle michelle = Michelle.getInstance();  Jack jack = michelle.createJack();  michelle.sayHello(jack);  > > 

The code above implements the friend class functionality in Java with two classes in different packages. Class Michelle acts as a friend class that accesses the class Jack members.

Hello! I am Jack from Delftstack 

Sheeraz is a Doctorate fellow in Computer Science at Northwestern Polytechnical University, Xian, China. He has 7 years of Software Development experience in AI, Web, Database, and Desktop technologies. He writes tutorials in Java, PHP, Python, GoLang, R, etc., to help beginners learn the field of Computer Science.

Related Article — Java Class

Источник

Записки программиста

Дружественые классы в Java — возможно ли это? И нужно ли вообще? На первый взгляд — казалось бы, не нужно. Ведь в Java отсутствует перегрузка операторов (а как правило, дружественные функции в программах на C++ — это функции-операторы). Но порой нужно, чтобы между классами были «особые отношения»: например, Class1 содержит некие поля
данных, но «не знает», как эти поля корректно загружать из файла какого-то формата или получать от пользователя. Зато ClassA знает, как загружать/сохранять данные в формате A, ClassB знает как загружать/сохранять данные в формате B, а ClassUser умеет эти данные получать от пользователя/передавать пользователю. На первый взгляд, можно сделать Class1 абстрактным классом, содержащим абстрактные методы для загрузки/сохранения полей данных, и в классах ClassA и ClassB реализовать эти методы. Теперь мы сможем загружать и сохранять данные обоих форматов. А если необходимо загрузить данные из формата A и сохранить их в формате B? И при этом некоторые из полей данных класса Class1 только для чтения?
Сформулируем задачу следующим образом. Классы данных и форматов не связаны родственными узами, поскольку они все же решают разные задачи: Class1 позволяет хранить данные и как-то ими манипулировать, а классы ClassA и ClassB позволяют сохранять/загружать данные различных форматов. Но при этом классы ClassA и ClassB должны иметь доступ на запись к тем полям Class1, которые остальные классы могут только читать. И такие отношения в парах классов Class1 — ClassA и Class1 — ClassB можно назвать если не дружественными, то , по крайней мере, приятельскими 🙂
Мое решение проблемы:

Помещаем классы Class1, ClassA, ClassB в один пакет. Содержимое файла Class1.java:

public abstract class Class1
public abstract int getOne();
public abstract int getTwo();
public abstract void setTwo( int _two);
public static Class1 getInstance()
< return new InnerFriend(); >
>

class InnerFriend extends Class1
private int one = 0, //Поле только для чтения
two = 0; //Поле для чтения и записи
public int getOne()
< return one; >
public int getTwo()
< return two; >
public void setTwo( int _two)
< two = _two; >
//Методы, доступные только дружественным классам:
public void setOne( int _one)
< one = _one; >
>

* This source code was highlighted with Source Code Highlighter .

Как видите, тут появился еще один персонаж — класс InnerFriend (внутренний друг, противоположность внутреннему врагу :)))) ). Внешние по отношению к пакету friends классы «не видят» его, поэтому не могут обратится к тем его методам, которые отсутствуют в публичном классе Class1. Классы, находящиеся в пакете friends, могут преобразовать ссылку на Class1 в ссылку на InnerFriend и, таким образом, получить доступ к методам, которых нет в Class1, но которые есть в InnerFriend. В качестве примера я создал 2 класса форматов — ClassA загружает/сохраняет данные текстового формата, ClassB загружает/сохраняет двоичные данные.

public class ClassA implements IFormat
public void loadData(Class1 _dta, String fname) throws IOException, FileNotFoundException
if (!(_dta instanceof InnerFriend) )
throw new IllegalArgumentException();
InnerFriend dta = (InnerFriend)_dta;
BufferedReader in = new BufferedReader( new FileReader(fname));
dta.setOne(Integer.parseInt( in .readLine()));
dta.setTwo(Integer.parseInt( in .readLine()));
in.close();
>
public void saveData(Class1 dta, String fname) throws IOException,
FileNotFoundException
PrintStream out = new PrintStream(fname);
out.println(dta.getOne());
out.println(dta.getTwo());
out.close();
>
>

* This source code was highlighted with Source Code Highlighter .

public class ClassB implements IFormat
public void loadData(Class1 _dta, String fname) throws IOException,
FileNotFoundException
if (!(_dta instanceof InnerFriend) )
throw new IllegalArgumentException();
InnerFriend dta = (InnerFriend)_dta;
DataInputStream in = new DataInputStream(
new FileInputStream(fname));
dta.setOne( in .readInt());
dta.setTwo( in .readInt());
in .close();
>
public void saveData(Class1 dta, String fname) throws IOException,
FileNotFoundException
DataOutputStream out = new DataOutputStream(
new FileOutputStream(fname));
out .writeInt(dta.getOne());
out .writeInt(dta.getTwo());
out .close();
>
>

Оба класса реализуют интерфейс IFormat:

public interface IFormat
void loadData(Class1 _dta, String fname) throws IOException,
FileNotFoundException;
void saveData(Class1 dta, String fname) throws IOException,
FileNotFoundException;
>
* This source code was highlighted with Source Code Highlighter .

Теперь можно написать небольшой тест, который будет читать данные из файла текстового формата, изменять поля, доступное для записи и сохранять измененные данные в файле двоичного формата:

public class TestFriends
public static void main( String args[]) throws java.io.IOException
Class1 data = Class1.getInstance();
System. out .println( «Read text file:» );
//Загрузка данных из текстового файла:
IFormat format = new ClassA();
format.loadData(data, args[0]);
System. out .println( «Value of one #0000ff»>out .println( «Value of two #008000″>//Изменяем одно из полей
data.setTwo(0x771177);
//Попытка изменить поле только для чтения
// fr1.setOne(0x771177);
// ((InnerFriend)fr1).setOne(0x771177);
//Сохранение в двоичном формате:
format = new ClassB();
format.saveData(data, args[1]);
>
>

* This source code was highlighted with Source Code Highlighter .

Источник

Оцените статью