Java поддерживает обобщения. Обобщенными бывают классы, методы и интерфейсы. Обобщения нужны для того, чтобы предоставить реализацию не зависящую от типов данных. Например, обобщенный класс может предоставлять обработку разнотипных данных. Обобщения работают только с объектами.
Обобщенные классыПри объявлении класса можно указать в угловых скобках классовые типы данных, которые можно использовать в данном классе при объявлении переменных, а также в методах. При объявлении переменной в угловых скобках указывают реальные типы, которыми будет оперировать данный класс. Например:
class Test <SomeType>{ SomeType obj;
Test(SomeType obj){ this.obj = obj; }
void ShowClassInfo(){ JOptionPane.showMessageDialog(null, obj.getClass().getName()); } }
class MyClass { private int a; void SomeMethod(){} }
class ArrayOperation <ItemClass extends Number>{ //переменная - массив для обработки private ItemClass[] arr; //конструктор класса public ArrayOperation(ItemClass[] arr) { this.arr = arr; }
public double GetMaxValueDouble(){ double max = Double.MIN_VALUE;
for(int i = 0; i < arr.length; i ++){ if(arr[i].doubleValue() > max) { max = arr[i].doubleValue(); } }
return max; }
public Integer GetMaxValueInteger(){ Integer max = Integer.MIN_VALUE;
for(int i = 0; i < arr.length; i ++){ if(arr[i].intValue() > max) { max = arr[i].intValue(); } }
return max; }
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
//в качестве параметра <SomeType> может быть только объект Test<Integer> t = new Test <Integer> (100); t.ShowClassInfo();
//сокращенная форма записи Test<String> t1 = new Test <> ("100"); t1.ShowClassInfo();
Object o = new Object(); Test<Object> t2 = new Test <Object> (o); t2.ShowClassInfo();
MyClass mc = new MyClass(); Test <MyClass> t3 = new Test <MyClass>(mc); t3.ShowClassInfo();
//присвоение двух переменных одного типа <Integer> Test<Integer> obj1 = new Test <Integer> (100); Test<Integer> obj2; obj2 = obj1; obj2.ShowClassInfo();
// переменные разных типов - присваивать нельзя // Test<Integer> obj3 = new Test <Integer> (100); // Test<String> obj4; // obj4 = obj3;
Integer[] arr1 = {1, 34, 567, 43, 122, 654, 1, 45}; ArrayOperation <Integer> ao1 = new ArrayOperation<Integer>(arr1); jTextArea1.append(ao1.GetMaxValueDouble() + "\n"); jTextArea1.append(ao1.GetMaxValueInteger() + "\n");
Double[] arr2 = {23.34, 12.0, 34.0, 245.56, 244.12}; ArrayOperation <Double> ao2 = new ArrayOperation<Double>(arr2); jTextArea1.append(ao2.GetMaxValueDouble() + "\n"); jTextArea1.append(ao2.GetMaxValueInteger() + "\n");
}
Для примера создадим иерархию классов - классы прямоугольники и круги и напишем обобщенный класс, которые работает с массивом фигур и выдает статистику — вычисляет максимальную, минимальну площадь фигуры и сумму площадей всех фигур. Класс статистики один а работает с разными классами за счет обобщений. Пример:
//абстрактный класс фигура abstract class Figure{ abstract public double getArea(); } //класс с описанием фигуры прямоугольника class Rectangle extends Figure{
public Rectangle(double width, double height) { this.width = width; this.height = height; }
public Rectangle() { this.width = 0; this.height = 0; }
private double width, height; @Override public double getArea(){ return width * height; } }
//класс с описанием фигуры круга class Circle extends Figure{
public Circle(double radius) { this.radius = radius; }
private double radius; @Override public double getArea(){ return 3.14 * radius * radius; } }
//класс с обобщенными параметрами для реализации подсчета сумм площадей фигур и //определения статистики по фигурам
class FigureStats <FigureType extends Figure>{
FigureType[] arr;
//в конструкторе - передаем массив фигур FigureStats(FigureType[] arr){ this.arr = arr; } //подсчитываем сумму площадей всех фигур public double getSumArea(){ double result = 0;
for(int i = 0; i < arr.length; i ++){ result = result + arr[i].getArea(); } return result; } //подсчитываем максимальную площадь фигуры public double getMaxArea(){ double maxarea = Double.MIN_VALUE;
for(int i = 0; i < arr.length; i ++){ if(maxarea < arr[i].getArea()) maxarea = arr[i].getArea(); } return maxarea; } //рассчитываем минимальную площадь фигуры public double getMinArea(){ double minarea = Double.MAX_VALUE;
for(int i = 0; i < arr.length; i ++){ if(minarea > arr[i].getArea()) minarea = arr[i].getArea(); } return minarea; } }
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Rectangle[] arr1 = new Rectangle[3]; arr1[0] = new Rectangle(1, 1); arr1[1] = new Rectangle(10, 10); arr1[2] = new Rectangle(5, 5);
//используем параметр типа Rectangle FigureStats <Rectangle> rs = new FigureStats <Rectangle>(arr1); jTextArea1.append("Суммарная площадь: " + rs.getSumArea() + " Максимальная площадь: "+ rs.getMaxArea()+ " Минимальная площадь: " + rs.getMinArea() + "\n");
Circle[] arr2 = new Circle[5]; arr2[0] = new Circle(1); arr2[1] = new Circle(10); arr2[2] = new Circle(4); arr2[3] = new Circle(100); arr2[4] = new Circle(0.1);
//используем параметр типа Circle FigureStats <Circle> cs = new FigureStats<>(arr2); jTextArea1.append("Суммарная площадь: " + cs.getSumArea() + " Максимальная площадь: "+ cs.getMaxArea()+ " Минимальная площадь: " + cs.getMinArea() + "\n"); }
Использование шаблонов аргументовЕсли мы хотим передать в метод переменную типа обобщенного класса, то возможны два варианта:
class FigureStats <FigureType extends Figure>{
FigureType[] arr;
//в конструкторе - передаем массив фигур FigureStats(FigureType[] arr){ this.arr = arr; }
//сравнение элементов массива. в метод мы можем передать только объект того типа, //который был передан при создании объекта. Этот способ не совсем гибок - //мы не можем передовать объекты другого типа. public boolean FirstisEqual(FigureStats <FigureType> another){ //если размеры массивов различны - массивы различны if(arr.length != another.arr.length){ return false; } else{ boolean found = false; int i = 0;
//пытаемся найти различные площади while(!found && i < another.arr.length){ if(arr[i].getArea() != another.arr[i].getArea()){ found = true; } i ++; } //если нашли различия - массивы различны if(found) return false; //иначе массивы равны else return true;
}
}
//этот метод имеет обобщенный параметр - мы можем передавать в него параметр //любого типа в отличие от предыдущего метода public boolean SecondisEqual(FigureStats <?> another){ //если размеры массивов различны - массивы различны if(arr.length != another.arr.length){ return false; } else{ boolean found = false; int i = 0;
//пытаемся найти различные площади while(!found && i < another.arr.length){ if(arr[i].getArea() != another.arr[i].getArea()){ found = true; } i ++; } //если нашли различия - массивы различны if(found) return false; //иначе массивы равны else return true;
}
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Rectangle[] arr1 = new Rectangle[3]; arr1[0] = new Rectangle(1, 1); arr1[1] = new Rectangle(10, 10); arr1[2] = new Rectangle(5, 5);
//создаем два объекта и передаем им одинаковые массивы FigureStats <Rectangle> fs1 = new FigureStats<Rectangle>(arr1); FigureStats <Rectangle> fs2 = new FigureStats<Rectangle>(arr1); //первый метод сравнения работает jTextArea1.append(Boolean.toString(fs1.FirstisEqual(fs2)) + "\n"); //второй метод сравнения работает jTextArea1.append(Boolean.toString(fs1.SecondisEqual(fs2)) + "\n");
Circle[] arr2 = new Circle[3]; arr2[0] = new Circle(1); arr2[1] = new Circle(2); arr2[2] = new Circle(3);
FigureStats <Circle> fs3 = new FigureStats<Circle>(arr2); //первый метод сравнения НЕ РАБОТАЕТ //jTextArea1.append(Boolean.toString(fs1.FirstisEqual(fs3)) + "\n"); //второй метод сравнения работает jTextArea1.append(Boolean.toString(fs1.SecondisEqual(fs3)) + "\n"); }
Обобщенные методыМетоды также могут быть обобщенными. Обобщенные методы принимают параметры или возвращают параметр обобщенного типа. Метод с обобщенными параметрами может содержаться в любом классе — обобщенном или не обобщенном. Пример:
class ObMethod{ static <T, V extends T > boolean isIn(T t, V[] v){ for(int i = 0; i < v.length; i ++){ if(t.equals(v[i])) return true; } return false; }
static <T, V extends T > T isIn1(T t, V[] v){ for(int i = 0; i < v.length; i ++){ if(t.equals(v[i])) return v[i]; } return null; } }
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Integer[] arr = {1, 2, 3, 45, 44}; jTextArea1.append(ObMethod.isIn(111, arr) + "\n"); String[] ras = {"Один", "Два" , "Три" , "Четыре" , "Пять"}; jTextArea1.append(ObMethod.isIn("111", ras) + "\n"); jTextArea1.append(ObMethod.isIn1("Три", ras) + "\n"); jTextArea1.append(ObMethod.isIn1("Т_ри", ras) + "\n"); }
Обобщенные интерфейсыВ Java поддерживаются обобщенные интерфейсы. Классы могут реализовывать обобщенные интерфейсы с указанием конкретных типов которые будут реализованы или также обобщенно. Пример:
//обобщенный интерфейс interface compare <T extends Number>{ T comp(T op1opitf, T op2); }
//класс, релизующий обобщенный интерфейс class TestClass1 implements compare <Integer>{ @Override public Integer comp(Integer op1, Integer op2){ if(op1.doubleValue() > op2.doubleValue()) return op1; else return op2; }; }
//класс, релизующий обобщенный интерфейс class TestClass2 implements compare <Double>{ @Override public Double comp(Double op1, Double op2){ if(op1.doubleValue() > op2.doubleValue()) return op1; else return op2; }; }
//класс, релизующий обобщенный интерфейс который сам является обобщенным class TestClass <T extends Number> implements compare <T>{ @Override public T comp(T op1, T op2){ if(op1.doubleValue() > op2.doubleValue()) return op1; else return op2; }; }
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { TestClass<Integer> t1 = new TestClass<Integer>(); jTextArea1.append(Integer.toString(t1.comp(100, 101)) + "\n"); //это сранение вызовет ошбку, так как работает контроль типов //jTextArea1.append(Integer.toString(t1.comp(1.1, 1.2)) + "\n"); //а вот так без ошибок: TestClass<Double> t2 = new TestClass<Double>(); jTextArea1.append(Double.toString(t2.comp(1.1, 1.2)) + "\n"); }
|