0

Дружественные классы в Java

Что такое "дружественные классы"? В C++, насколько я знаю, если класс Class1 объявляет своим другом Class2, то Class2 может обращаться ко всем (в том числе и private-) полям и методам Class1:
class Class1
{
  friend: Class2;
private:
  int a, b;
};

class Class2
{
  public:
  changer(Classs1 *data int _a, int _b);
};

void Class2::changer(Classs1 *data int _a, int _b)
{
    data->a = _a;
    data->b = _b;
}

* This source code was highlighted with Source Code Highlighter.

Дружественые классы в 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:

package friends;

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 загружает/сохраняет двоичные данные.

package friends;
import java.io.*;

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.


package friends;
import java.io.*;

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();
  }
}


* This source code was highlighted with Source Code Highlighter.


Оба класса реализуют интерфейс IFormat:
package friends;
import java.io.*;

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.


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

import friends.*;

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 = " + data.getOne());
    System.out.println("Value of two = " + data.getTwo());
  //Изменяем одно из полей
    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.


0 коммент.:

Отправить комментарий