Страницы

Поиск по вопросам

Показаны сообщения с ярлыком generics. Показать все сообщения
Показаны сообщения с ярлыком generics. Показать все сообщения

понедельник, 13 апреля 2020 г.

Использование generics в Delphi: неизвестный тип данных TList

#delphi #generics #delphi_xe2

                    
Пробую использовать generics:

var List: TList;    


но RAD Studio мне подчеркивает неизвестный тип данных TList, хотя в коде var List:
TList; - никаких проблем нет. В чем тут может быть проблема? Юнит Classes, естественно,
подключен.
    


Ответы

Ответ 1



Ответ найден: нужно подключить еще и юнит System.Generics.Collections

вторник, 31 марта 2020 г.

Разные перегрузки универсального метода в зависимости от указанного типа

#c_sharp #generics #методы


товарищи! Наткнулся я тут на старенькое интеревью с Джоном Скитом на хабре. В его
рамках Джон задал читателям следующий вопрос: 


  Как вы можете заставить этот код скомпилироваться, так чтобы он вызвал
  три различные перегрузки метода?


void Foo()
{
    EvilMethod();
    EvilMethod();
    EvilMethod();
}


Мне не совсем понятно, как можно реализовать такую вещь, ведь даже если использовать
where, то мы получаем ошибку, что метод с таким же названием и параметрами уже существует.
На SO искал, в Google тоже (возможно, искал плохо, так что если что - палками не бейте))
Будет очень интересно выслушать ваши идеи, так что заранее спасибо)
    


Ответы

Ответ 1



Удалось сделать только так: public interface IEvilMethod { void EvilMethod(); } public class Example : IEvilMethod, IEvilMethod, IEvilMethod { public void Foo() { ((IEvilMethod) this).EvilMethod(); ((IEvilMethod) this).EvilMethod(); ((IEvilMethod) this).EvilMethod(); } void IEvilMethod.EvilMethod() { Console.WriteLine(typeof(T)); } void IEvilMethod.EvilMethod() { Console.WriteLine(typeof(T)); } void IEvilMethod.EvilMethod() { Console.WriteLine(typeof(T)); } } Update Нашел решение здесь. public class ReferenceGeneric where T : class { } public class EvilClassBase { protected void EvilMethod() { Console.WriteLine("int?"); } } public class EvilClass : EvilClassBase { public void Run() { EvilMethod(); EvilMethod(); EvilMethod(); } private void EvilMethod(ReferenceGeneric arg = null) where T : class { Console.WriteLine("string"); } private void EvilMethod(T? arg = null) where T : struct { Console.WriteLine("int"); } }

Rust: преобразование обобщённого типа в конкретный

#generics #rust #преобразование_типов


Собственно код:

extern crate num_traits;

pub trait DigitCount {
    fn decimal_digit_count(&self) -> usize;
}

impl > DigitCount for T {
    fn decimal_digit_count (&self) -> usize {
        if self.is_zero() {
            1
        } else {
            self.into().abs().log10().trunc() as usize + 1
        }
    }
}


Метод decimal_digit_count должен вызываться для любого целого значения и возвращать
количество десятичных разрядов в нём:

println!("{}", 123u64.decimal_digit_count());


Вопрос: как правильно записать преобразование T → f64 для последующего вызова методов
abs, log10, trunc? В текущем виде компилятор требует аннотацию типа для into(), но
если её подставить (into::()), то говорит, что ожидается 0 параметров типов.
    


Ответы

Ответ 1



У самого метода и правда нет типового параметра, потому что он есть у типажа: pub trait Into { fn into(self) -> T; } Первый вариант исправления - просто явно указать через промежуточную переменную какой тип мы в итоге хотим получить: let f: f64 = self.into(); f.abs().log10().trunc() as usize + 1 (playpen) Второй вариант - использовать UFCS форму вызова метода с явным обозначением T у типажа: Into::::into(self).abs().log10().trunc() as usize + 1 (playpen) Метод decimal_digit_count должен вызываться для любого целого значения и возвращать количество десятичных разрядов в нём На всякий случай еще добавлю что преобразование в f64 реализовано не для всех целых типов. Такое преобразование обязано всегда заканчиваться успехом, что невозможно, например, для u64 или i128 из-за невозможности вместить весь диапазон значений.

воскресенье, 8 марта 2020 г.

Универсальный класс с параметром в виде универсального класса

#c_sharp #generics


Возможно ли, и если да, то как, создать универсальный (generic) класс, параметром
для которого выступает другой универсальный класс? 

Псевдо-код поясняющий идею:

class Gen where T : class { }   // тут всё ОК
class MoreGen where G : Gen { } // здесь непонятно как сделать


Хочется ограничить параметры для MoreGen только классами Gen, чтобы была возможность
использовать следующий код:

new MoreGen>(); // Должно быть OK
new MoreGen();        // Нужна ошибка компиляции
new MoreGen>();      // Нужна ошибка компиляции

    


Ответы

Ответ 1



На основании упомянутого в комментариях ответа на английском SO, решение "в лоб" не является возможным. Для достижения желаемого эффекта приходится добавлять интерфейс: interface IGen {} class Gen : IGen where T : class { } class MoreGen where G : IGen { } public class Test { public static void Main() { new MoreGen>(); // OK new MoreGen>(); // OK // new MoreGen(); // no implicit reference conversion from `string' to `IGen' // new MoreGen>(); // The type `int' must be a reference type } } Результат выполнения кода

Имплементация интерфейса с generic type

#java #generics #интерфейс


Помогите с реализацией интерфейса. Допустим у нас есть интерфейс 

public interface Sorter> {
    void sort(List list);
}


и я хочу создать класс, который будет реализовывать этот интерфейс используя genec
types. Я пробовала так:

public class SorterImpl> implements Sorter {
    public void sort(List list){
        boolean erijuht1 = true; 
        boolean erijuht2 = true; 
        for(int a=0; a list.get(a+1)){
                erijuht1 = false;
            }
            if(list.get(a) < list.get(a+1)){ 
                erijuht2 = false;
            }
        }
    }
}


Проблема в том, что последний класс не хочет сравнивать объекты между собой.
Как мне реализовать данный интерфейс так, что бы класс был и с generic type, и мог
сравнивать объекты?
    


Ответы

Ответ 1



Вы не можете в данной функции сравнивать два объекта таким образом, так как не понятно какой из них считать большим, а какой меньшим (и равным). list.get() возвращает Вам объект класса T, наследуемый от Comparable. Значит мы можем использовать вызывать все методы этого класса, а в данном случае compareTo (возвращает 1,0 или -1). Осталось добавить код для перемены мест в случае если "больший" имеет меньший индекс. Вот что получилось: public class SorterImpl> implements Sorter { public void sort(List list) { // boolean erijuht1 = true; // boolean erijuht2 = true; for (int j = 0; j < list.size() - 1; j++) for (int a = 0; a < list.size() - j - 1; a++) { if (list.get(a).compareTo(list.get(a + 1)) > 0) { // erijuht1 = false; } else { // erijuht2 = false; T temp = list.get(a + 1); list.set(a + 1, list.get(a)); list.set(a, temp); } } } //Тестируем public static void main(String[] args) { List ar = new ArrayList() { { add(2); add(3); add(5); add(8); add(2); } }; SorterImpl s = new SorterImpl(); s.sort(ar); for (int i = 0; i < ar.size(); i++) { System.out.print(ar.get(i).toString() + " "); } } } Результат: 8 5 3 2 2

среда, 4 марта 2020 г.

В чём разница: Class a & Class<?> a?

#java #generics


Скажите пожалуйста, чем отличаются два случая:

public class Main {
    public static void main(String[] args) {
        Class c1 = String.class;      // Строка 3
        Class c2 = String.class;   // Строка 4
        System.out.println(c1 +", " + c2);
    }
}


В 3-ей и 4-ой строках мы делаем что-то похожее, но компилятор предупреждает, что
c1 будет ссылкой на объект сырого типа, а c2 для компилятора -- прекрасная ссылка на
прекрасный объект Class  

Не понимаю разницы. И сырой тип и параметризованный знаком вопроса одинаково заполнены
классом Object во время выполнения. Да и во время компиляции тот и другой обрабатываются
по равному...

Возможно это просто философское предупреждение от Java, что сырой тип он сбивает
с пути истины, а Class сразу показывает, что программист нацелен пихать в c2 всевозможные
классы?
    


Ответы

Ответ 1



Брюс Эккель в "Философия Java" пишет, что использование Class вместо Class предпочтительнее, хотя оба варианта эквивалентны за тем лишь исключением, что при компиляции кода, где используется Class, будет выдаваться предупреждение. Кроме этого Эккель отмечает, что использование Class указывает, что разработчик более осмысленно подошёл к выбору типа, а не используете просто Class по незнанию.

пятница, 28 февраля 2020 г.

Как создать объект типа T

#java #generics


У меня есть метод с параметром

 void testMethod(Consumer arg) {
  T t = ???;
  arg.accept(t);
}


Как создать объект типа T при условии что T имеет конструктор по умолчанию?

Вот код, который я испытывал

public class MethodConsumer {
  private  void test(T t) {  // Consumer
    System.out.println("test()"+t);
  };

   void testMethod(Consumer arg) {
    T t = null;
    arg.accept(t);
  }

  public static void main(String[] args){
    MethodConsumer test = new MethodConsumer();
    test.testMethod((t) -> test.test(t));
  }

}

    


Ответы

Ответ 1



Вообще то, то что вы пытаетесь сделать запрещено, читайте туториал You cannot create an instance of a type parameter. Оно и понятно, шаблон не живет сам по себе, он живет только с конкретным типом. В вашем случае рабочий код может выглядеть приблизительно так: void testMethod(Consumer arg, Class clazz) { T t = clazz.newInstance(); //надо ловить эксепшн arg.accept(t); } Обратите внимание, что во втором параметре мы указываем тип T А вызов его типа, так: String s="blabla"; test.testMethod(arg, s.class);

Ответ 2



void testMethod(Consumer arg) { T t = ???; arg.accept(t); } В этом случае попытка создать новый экземпляр класса противоречит самой идее шаблонов. Шаблоны нужны для того, чтобы отвязать алгоритм работы с данными от конкретной реализации данных. Например LinkdList'у без разницы что у него внутри, алгоритм для всех типов данных один и тот же. Шаблон работает с любым типом данных, но нельзя создать объект любого типа, потому что непонятно что создавать. Если вам нужно работать с каким-то конкретным типом данных, то его и используйте. public class MethodConsumer { class MyClass{ } private void test(T t) { // Consumer System.out.println("test()"+t); }; void testMethod(Consumer arg) { MyClass t = new MyClass(); arg.accept(t); } public static void main(String[] args){ MethodConsumer test = new MethodConsumer(); test.testMethod((t) -> test.test(t)); } }

понедельник, 24 февраля 2020 г.

Как создать типизированный массив объектов?

#java #массивы #коллекции #generics


Предыстория: пишу свою реализацию коллекции на основе массива (задание такое). И
дошел до метода toArray().

Есть классический массив:

private E[] array;

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

Такой вариант при возвращении все равно возвращает Object[] 

array =(E[]) new Object[length]

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


Ответы

Ответ 1



Тут есть подробное описание проблемы на английском языке. Вроде должно сработать как-то так: public E[] getArray(Class clazz, int size) { @SuppressWarnings("unchecked") E[] arr = (E[]) Array.newInstance(clazz, size); return arr; }

Ответ 2



При инициализации массива в Java вы изначально объявляете его тип. В памяти хранятся объекты именно этого типа, и присвоить внутрь него объекты другого типа, никак не получится. Скорее всего ваша проблема в том, что в каком то месте вы преобразовываете тип вашего массива в массив типа Object. Убедитесь что вы верно указали все типы переменных, а так же типы всех возвращаемых значение функций. Вот простой пример generic класса, работающего с массивом: public class MyStructure{ private T[] array; public MyStructure(T[] array) { this.array = array; } public T[] getArray() { return array; } } Обратите внимание, на то что у функции getArray() указан тип T[].

Ответ 3



Информация о generic'a в java не доступна после компиляции. Все типы стираются и превращаются в Object, а type safe обеспечивается явным кастованием. Приведу пример. У нас есть метод: final T field; private T get() { return field; } После компиляции информация о типах сотрется и будет следующее: final Object field; private Object get() { return field; } А там, где вызывался метод будет следующее: GenericClassobj=new GenericClass<>(); String field= (String) obj.get() Соответственно, конструкция вида (T[])new Object[] является вполне валидной.

воскресенье, 2 февраля 2020 г.

Шаблонная функция C#

#c_sharp #cpp #generics #шаблоны_с++


Требуется реализовать шаблонную функцию сложения на C#

Нужно разливать эту функцию из C++ на C# 

template
T sum(T a, T b)
{
    return a + b;
}


Вот эта запись выдает ошибку что операция сложения не перегружена над типом Т

public static T sum(T a, T b)
{
    return a + b;
}

    


Ответы

Ответ 1



public static T sum(T a, T b) { dynamic da = a, db = b; return da + db; } Console.WriteLine(sum(10, 20)); //30

Ответ 2



К сожалению такого же элегантного способа в C# нет, так как в этом языке для обобщений не предусмотрены ограничения по наличию оператора. В качестве несколько неуклюжей альтернативы можно предложить разве что написание обобщённых методов, заменяющих операторы, таких, как Add, Multiple и тд

суббота, 1 февраля 2020 г.

Возврат объекта типа T

#c_sharp #net #generics


Имеется такой код:
public static FrameworkElement GetParentOfType(FrameworkElement element)
{
    if (element is T) return element;
    while (true)
    {
        element = element.Parent as FrameworkElement;
        if (element is T) break;
    }
    return element;
}
...
MyPanel myPanel = (MyPanel)GetParentOfType(myTextBox);
MyLabel myLabel = (MyLabel)GetParentOfType(myTextBox);
...

Но мне хотелось бы сделать что то вроде этого:
public static T GetParentOfType(FrameworkElement element)
{
    if (element is T) return element as T;
    while (true)
    {
        element = element.Parent as FrameworkElement;
        if (element is T) break;
    }
    return element as T;
}
...
MyPanel myPanel = GetParentOfType(myTextBox);
MyLabel myLabel = GetParentOfType(myTextBox);
...

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


Ответы

Ответ 1



Надо вот как: public static T GetParentOfType(FrameworkElement element) where T : FrameworkElement { if (element is T) return element as T; while (element != null) { element = element.Parent as FrameworkElement; if (element is T) break; } return element as T; }

Ответ 2



Попробуй что-то такое public T ConvertTo(SomeType val) { return (T)Convert.ChangeType(val, Type.GetTypeCode(typeof(T))); } Т.е. использовать принудительное преобразование типа, а не оператор as

пятница, 31 января 2020 г.

Дженерики, ошибка “Wrong 2nd argument type”

#java #generics


public static  boolean addToGroupMap(K key, V value, Map>
checkMap){

    assert checkMap!=null;
    boolean result = false;

    Collection vList = checkMap.get(key);
    if (vList==null){
        checkMap.put(key, new ArrayList(Collections.singleton(value)));
    }else {
        vList.add(value);
        result = true;
    }
    return result;
}


Думал, что более менее разобрался с дженериками, однако, в упор не понимаю, почему
в строке: checkMap.put(key, new ArrayList(Collections.singleton(value))); Компилятор
выдает ошибку:


  Wrong 2nd argument type. Found: 'java.util.ArrayList', required: '? extends java.util.Collection


Сначала в сигнатуре метода было : K key, V value, Map> checkMap. Однако
теперь надо, чтобы в значении мапы была именно коллекция. При этом, нет желания менять
в куче кода List<> в checkMap'e на Collection<>, да и почему это надо делать, если
лист имплементирует коллекцию?

UDP: Также, возникает вопрос. Почему, если заменить в сигнатуре ? extends Collection
на ? super Collection, пропадает доступ к методам коллекции? Ведь вроде ? super Collection
ограничивает самой коллекцией и ее родителями?
    


Ответы

Ответ 1



Забудем пока про конкретно ваш код и рассмотрим более простой пример. Допустим, есть вот такие классы: // Для демонстрации иерархии типов class A { } class B extends A { } class C extends B { } // Для демонстрации контейнера class S { private V value; public V get() { return value; } public void set(V value) { this.value = value; } } Рассмотрим такой контейнер, как S. Каким будет возвращаемое значение для метода get? Этот метод вернет что-то, что является наследником B. То есть, что бы метод get ни вернул - это что-то можно записать в переменную типа B. S s; B v = s.get(); // Значение типа ? extends B всегда можно записать в переменную типа B Теперь рассмотрим метод set. Кажется, что все нормально? Но давайте сделаем вот так: S s = new S(); s.set(new B()); // Ошибка - значение типа B не может быть передано как параметр типа C Здесь я создал конкретный контейнер для примера. Даже если на самом деле используется S - компилятор должен обеспечить корректность кода в любом случае. Теперь рассмотрим такой контейнер как S. Метод set у него можно вызвать с параметром типа B: S s; s.set(new B()); // Значение типа B всегда можно передать как ? super B А вот метод get нормально вызвать не получится: S s = new S(); B v = s.get(); // Ошибка - попытались значение типа A записать в переменную типа B В итоге получается, что get-методы требуют отношения extends, а set-методы требуют отношения super. Если вашему коду нужно использовать оба типа методов - придется определять оба отношения одновременно, т.е. оставить просто . Возвращаюсь к вашему коду, можно заметить, что из параметра checkMap вы одновременно получаете данные (get) - и передаете их ему (put). Поэтому, вы не можете использовать ? extends ... в определении параметра. Единственный способ сделать оба вызова рабочими - использовать конкретный тип данных. Например, Map>. Если же вам нужно работать с разными отображениями - то сам тип коллекции надо делать обобщенным: Map где C extends Collection. Для того чтобы создать такую коллекцию, вам нужно будет принимать фабрику или класс как параметр: public static > boolean addToGroupMap(K key, V value, Map checkMap, Callable collectionFactory) { C vList = checkMap.get(key); checkMap.put(key, vList = collectionFactory.call()); } public static > boolean addToGroupMap(K key, V value, Map checkMap, Class collectionClass) { C vList = checkMap.get(key); checkMap.put(key, vList = collectionClass.newInstance()); } Смысл фабрики - в том, что ее создает тот код, который знает точный тип коллекции: Map> checkMap1 = new Map<>(); Callable> factory1 = () -> new ArrayList(); Map> checkMap2 = new Map<>(); Callable> factory2 = () -> new HashSet(); Дальше фабрика по цепочке вызовов передается пока не придет в метод addToGroupMap.

вторник, 28 января 2020 г.

Правильное использование ограничений в обобщённых типах

#c_sharp #generics


Есть интерфейс IRepository:

public interface IRepository where T : BaseModel {...}


где BaseModel- базовый класс для моделей.
Есть провайдер к базе данных, который реализует этот интерфейс:

public class Provider : IRepository where T : BaseModel {...}


Есть ли необходимость указывать ограничение на T в случае провайдера, если оно уже
указано на уровне интерфейса?
    


Ответы

Ответ 1



Ну очень странный вопрос. Да нужно, потому что без него не компилируется: http://ideone.com/zfhSDo public class Provider : IRepository /* where T : BaseModel */ {} prog.cs(6,14): error CS0314: The type `T' cannot be used as type parameter `T' in the generic type or method `IRepository'. There is no boxing or type parameter conversion from `T' to `BaseModel' prog.cs(5,30): (Location of the symbol related to previous error) http://ideone.com/I26aUP public class Provider : IRepository where T : BaseModel {} А тут всё хорошо.

Ответ 2



Если ваш вопрос на самом деле: почему ограничения на тип-параметр не наследуются от интерфейса — то вот ответ на этот вопрос Эрика Липперта, одного из (бывших) разработчиков языка. Вкратце: потому что в общем случае нету однозначного соответствия параметров класса и параметров интерфейса. Например, рассмотрим такое объявление: class X where P : ISomething where Q : Base { } class Provider : IRepository>> { } У нас есть констрейнт на X>, но как превратить его в эффективный констрейнт на T, непонятно.

воскресенье, 26 января 2020 г.

Java generics как работает?

#java #generics


Я пишу:

Class a = n.getClass();


Метод getClass() должен вернуть объект описывающий класс и присвоить его переменной а.

Но вот это часть мне не понятна Class. Что это за тип переменной
такой и куда переменную а теперь вообще можно использовать?
Хочу понять как это работает. Помогите разобраться. 
особенно  знак вопроса убивает здесь что-то общее с тернарной операцией?
    


Ответы

Ответ 1



Примеры Обозначение типа параметра public static double sumOfList(List list) { double s = 0.0; for (Number n : list) s += n.doubleValue(); return s; } public static void printList(List list) { for (Object elem: list) System.out.print(elem + " "); System.out.println(); } Поле класса и тип возвращаемого значения public class ClassContainer { private List list; public ClassContainer(List list){ this.list = list; } public List getList(){ return this.list; } }

Ответ 2



Нет, что вы, нет здесь тернарной операции)) ? extends Object - означает, что здесь может быть любой класс, унаследованный от Object. Вот аналогичная запись: List>

Ответ 3



В коде где используются обобщённые типы знак вопроса ? называется шаблоном поиска (wildcard) и представляет собой неизвестный тип. Этот символ используется в различных ситуациях: обозначает тип параметра, поля класса или типа локальной переменной, тип возвращаемого значения. Есть определённые ограничения его применения.

Java модификаторы доступа

#java #generics


С учебными целями создаю собственные реализации некоторых структур данных. Надо создать
обычное бинарное дерево поиска, затем от него унаследовать красно-чёрное и другие самосбалансированные.
Для каждого типа дерева есть два класса: собственно дерево и узел (поскольку узлы обычного
и красно-чёрного деревьев различаются — второй содержит ещё информацию о цвете). Соответственно
просто унаследовать от обычного дерева красно-чёрное не получится.

Я пытаюсь решить это проблему при помощи обобщённых классов. Класс GenTree выглядит
следующим образом: 

package BasicTrees;

public abstract class GenTree> 
    implements Iterable {

    protected NodeType root;

    protected abstract NodeType newNode(KeyType key, Type value);
    ...
}


в этом классе реализованы операции вставки, поиска, удаления, итератор для обычного
дерева. Метод newNode реализуется уже в каждом конкретном типе дерева, и служит в дальнейшем
вместо конструктора для создания новых узлов.
Класс GenNode выглядит следующим образом:

package BasicTrees;

public class GenNode> {

    protected final KeyType key;
    protected Type value;
    protected NodeType left;
    protected NodeType right;
    protected NodeType parent;

    protected GenNode(KeyType key, Type value) {
        this.key = key;
        this.value = value;
    }
}


После этого обычное дерево создаётся тривиально классами Tree:

package BasicTrees;

public class Tree
        extends GenTree> {

    @Override
    protected Node newNode(KeyType key, Type value) {
        return new Node<>(key, value);
    }
}


и Node: 

package BasicTrees;

public class Node
        extends GenNode> {

    protected Node(KeyType key, Type value) {
        super(key, value);
    }
}


пакет BasicTree также содержит класс TreeIterator.

Для создания красно-чёрного и других самосбалансированных деревьев требуется создать
отдельный пакет RBTrees, чтобы они не путались. Классы RBTree и RBNode наследуются
от GenTree и GenNode аналогично. В классе RBNode добавляется поле, отвечающее за цвет
узла. В классе RBTree требуется переписать методы, отвечающие за вставку и удаление
узлов, метод поиска и итератор остаются теми же. Для этого переписанные методы класса
RBTree должны иметь доступ к protected полям left, right, parent, объявленным в классе
GenNode и унаследованным RBNode. Но модификатор protected доступа к ним не даёт, так
как RBTree не является наследником GenNode и не находится с ним в одном пакете.

Можно ли как-то дать доступ к защищённым полям наследников GenNode для наследников
GenTree, не давая его всей программе? Или единственным выходом будет создание в классе
RBNode геттеров и сеттеров с видимостью пакета, а классе RBTree их использовать? Но
тогда придётся их создавать каждый раз при создании нового типа дерева. Имеет ли смысл
создание этих геттеров и сеттеров с модификатором protected в классе GenNode? В таком
случае они создаются один раз.

Хотелось бы услышать мнение сообщества на этот счёт, а также замечания по структуре
программы в целом. Был бы рад узнать, можно ли мою задумку реализовать проще.

Спасибо за возможность задать вопрос и подумать над ним в процессе написания. Вариант
с геттерами и сеттерами в GenTree (который мне кажется оптимальным) я придумал уже
в процессе когда писал.
    


Ответы

Ответ 1



Я бы всё разместил в одном пакете, а классы, связанные с конкретным типом дерева, сделал бы вложенными классами. И активно бы использовал package-private доступ. Именно так реализованы, например, HashMap и LinkedHashMap в OpenJDK. Они довольно интенсивно переиспользуют код друг друга, переиспользуемые поля, методы и классы все объявлены package-private. В целом package-private и организация кода с помощью вложенных классов — это очень хорошо. Не факт, например, что вы вообще хотите выставлять на публику базовый класс GenTree. Опубликовав его одиножды, вы навсегда будете привязаны к нему. А вдруг в дальнейшем рефакторинге вы захотите его сильно переделать (например, добавить поддержку небинарных деревьев) или вообще от него отказаться? По большому счёту это деталь реализации. Поэтому его тоже можно сделать package-private.

Ответ 2



Раз уж одному типу дерева соответствует один тип узлов, то можно делать конкретную реализацию узла внутренним классом его дерева. Тогда данный тип дерева будет иметь доступ к его закрытым (private) полям и методам. Узел при этом по-прежнему может наследоваться от абстрактного узла.

пятница, 24 января 2020 г.

Пример по generics из Философии Java

#java #generics


Приветствую всех! Читаю у Брюса Эккеля про дженерики, попался в качестве примера
такой код:     

 package net.mindview.util;
 import java.util.*;
 public class New {

 public static  Map map() {
      return new HashMap();
   }
 }

 public class LimitsOfInference {
     static void f(Map> petPeople) {}

     public static void main(String[] args) {
          // f(New.map()); // Does not compile
     }
 }


И объяснение:


  Type inference doesn’t work for anything other than assignment. If you pass the
result of a
  method call such as New.map( ) as an argument to another method, the compiler will not
  try to perform type inference. Instead it will treat the method call as though
the return value
  is assigned to a variable of type Object. 


И тем не менее я воспроизвел этот код в IDE, он успешно скомпилировался и запустился.
Это ошибка или информация в книге устарела? У меня jdk 8.
    


Ответы

Ответ 1



Вся проблема в том, что у вас Java 8. Укажите среде разработки, что нужно собрать это для Java 6, и вы увидите, что проект действительно не компилируется. Все дело в даймонд операторах, которых в Java 6 еще не было, соответсвенно, создавая f(New.map()) считается, что это map с двумя objecт, а ожидается параметризованный другими типами Map. Чтобы понять проблему, давайте возьмем пример попроще. List = new ArrayList <>(); Здесь после листа дженерик со стрингом, почему-то не отображается правильно... Это вполне допустимо для Java 8, поскольку Java 8 видит в левой части параметризованный стрингами лист и сама дописывает в дженерик левой части тот же стринг в качестве параметра. Однако, если запустить это на Java 6, то будет ошибка компиляции, потребуются явные параметры дженериков `List = new ArrayList ();` То-же самое происходит в Вашем примере

Приведение параметризованных типов

#java #generics #типы


В сервлете необходимо получить инстанс DAO который обернут в AtomicReference, но
при приведении типов компилятор волнуется и пишет:

Unchecked cast: 'java.lang.Object' to 'java.util.concurrent.atomic.AtomicReference'
less... (⌘F1) 
Signals places where an unchecked warning is issued by the compiler, for example:

  void f(HashMap map) {
    map.put("key", "value");
  }

Hint: Pass -Xlint:unchecked to javac to get more details.


Сам код выглядит так:

AtomicReference dao = (AtomicReference) req.getServletContext().getAttribute("dao");


Подскажите как можно успокоить компилятор, учитывая что instance of не работает с
дженериками, или если работает то как это написать?

Спасибо.
    


Ответы

Ответ 1



Использовать аннотацию @SuppressWarnings("unchecked").

суббота, 11 января 2020 г.

Импорт данных секвенирования и обработка SAM-файлов в TDictionary

#delphi #generics #биоинформатика


До сих пор, работая непосредственно с SAM-файлами, мы использовали массивы. Сейчас
хотим перейти на TDictionary, поскольку он гораздо удобнее в плане поиска. Возникают
вопросы:
1. Иерархия словаря. Как я вижу себе структуру, которая была бы удобна для поиска:

type
DataType = record
  name,qual, info, seq:string;
  data:array[1..30] of double;
  paired, structured:boolean;
  ref:integer;
end;

TData3 = TDictionary;  // по имени референса
TData2 = TDictionary; // по позиции внутри хромосомы
TData1 = TDictionary; по хромосомам

var
  dd: TData1;


тогда я могу достаточно быстро пробежаться вниз по дереву chr->pos->ref и все записи
будут уникальны. А для работы с переменной dd создаю свой класс, где будут все рутины.
Прав ли я, предполагая такую структуру?


Как правило, в большинстве биоинформатических утилит для обозначения хромосом используются
строковый тип ['1'...'22','X','Y','MT']. Формально, я могу TData1 определить как 



type 
   TData1 = TDictionary ; по хромосомам



но боюсь, что выигрывая в удобстве, потеряю в скорости поиска. Так ли это?

Спасибо заранее за помощь!
    


Ответы

Ответ 1



Вначале отвечу на второй вопрос. По опыту, разницы в скорости не наблюдается. Более того, удобнее оставлять сведения о хромосоме именно в текстовом виде, так как порой приходится иметь дело со сложными именованиями хромосом, а не все утилиты поддерживают таблицу номерного соответствия. Более того, я предлагаю совместить первый и второй уровень вашего словаря, сделав ключом сразу полную координату {chr, pos} в виде строки формируя её либо так Key:= chr + ',' + IntToStr(pos), либо так Key:= chr + #9 + IntToStr(pos). Первый случай удобен для экспорта в Excel или BED, второй - для SAM/BAM. Скорость поиска возрастёт, неудобств при формировании ключа - минимум. И ещё, отдельный класс создавать, на мой взгляд, не нужно. Наследуйте непосредственно TDictionary, так будет гораздо удобнее. Суммируя всё вышенаписанное, предлагаю сделать так: type DataType = record name,qual, info, seq:string; data:array[1..30] of double; paired, structured:boolean; ref:integer; end; TData3 = TDictionary; // по имени референса TData1 = class(TDictionary); по хромосоме и позиции, ключ строится, как описано выше // переменные и методы класса, к данным первого уровня удобно обращаться без разъименования. end; И встречный вопрос: ключ в TData3 и name в DataType у вас не одно и то же?

Можно ли данные в дереве TDictionary писать в файл, как массив?

#delphi #generics


Вопрос, непосредственно связанный с моим предыдущим Импорт данных секвенирования
и обработка SAM-файлов в TDictionary

Ранее, когда мы держали данные для обработки в массиве записей, мы просто создавали
типовой файл и сбрасывали данные (а затем архивировали в ZIP, хорошо сжималось):

type
  data = record
   <...>
   end;
var
  f: file of data;
  d: array of data;


Теперь, если мы будем использовать структуру 

type
DataType = record
  name,qual, info, seq:string;
  data:array[1..30] of double;
  paired, structured:boolean;
  ref:integer;
end;

TData3 = TDictionary;  
TData1 = class(TDictionary);
   end;


Можно ли использовать тот же подход, что и раньше, т.е. сбрасывать данные в типовой
файл? Или необходимо использовать FileStream?
    


Ответы

Ответ 1



Можно конвертировать в массив, но, подозреваю, что не нужно. Особенно, если вы планируете делать это лишь для записи. Гораздо проще работать с потоками. Могу привести пример, как это реализовано у нас в базовом классе: TVCFFile = class (TDictionary) // private <...> procedure SaveToStream(stream: TStream); protected <...> public <...> procedure SaveToZip(const AFileName: string); override; procedure SaveToFile(const AFileName: string); override; end; procedure TVCFFile.SaveToStream(stream: TStream); var posi: System.Generics.Collections.TPair; pair:System.Generics.Collections.TPair; third:System.Generics.Collections.TPair; i: Integer; writer: TWriter; begin writer := TWriter.Create(stream, 4096); try writer.WriteListBegin; for third in Self do begin writer.WriteString(third.Key); writer.WriteListBegin; for pair in third.Value do begin writer.WriteInteger(pair.Key); writer.WriteListBegin; for posi in pair.Value do begin writer.WriteString(posi.Key); writer.WriteDouble(posi.Value.qual); writer.WriteString(posi.Value.alt); writer.WriteString(posi.Value.filter); writer.WriteString(posi.Value.info); end; writer.WriteListEnd; end; writer.WriteListEnd; end; writer.WriteListEnd; finally writer.Free; end; end; procedure TVCFFile.SaveToZip(const AFileName: string); var stream: TStream; zipFile: TZipFile; begin stream := TMemoryStream.Create; try SaveToStream(stream); stream.Position := 0; zipFile := TZipFile.Create; try zipFile.Open(AFileName, zmWrite); zipFile.Add(stream, 'bigdata.tvb'); zipFile.Close; finally zipFile.Free; end; finally stream.Free; end; end; procedure TVCFFile.SaveToFile(const AFileName: string); var stream: TStream; begin stream := TFileStream.Create(AFileName, fmCreate); try SaveToStream(stream); finally stream.Free; end; end; Можно просто записать в файл, можно сразу в ZIP-файл, скорость записи и считывания достаточно велика (для загрузки есть аналогичные методы, строите их от обратного). Плюс, понадобятся методы для загрузки из SAM, записи в него же, возможно, понадобится поддержка BED, VCF и прочее, а, может, вы всё это сделаете в других классах.

пятница, 10 января 2020 г.

Реализовать generic для двух интерфейсов

#java #generics


Как реализовать generic для двух интерфейсов с возможностью затем использовать его
в качестве любого из этих типов. Интересует использование этого generic для элементов
List. 

Код для понимания, что у меня не получается и что я хочу получить в конечном итоге:

public class Container {

    private List mList;

    public Container() {
        mList = new ArrayList();
    }

    public List getIFooList() {
        return mList;
    }

    public List getIBarList() {
        return mList;
    }

    public interface IFoo {
        void foo();
    }

    public interface IBar {
        void bar();
    }

    public static class MultiInterfacesClass implements IFoo, IBar {


        @Override
        public void bar() {

        }

        @Override
        public void foo() {

        }
    }
}

    


Ответы

Ответ 1



Можно написать public List getIFooList() { return mList; } List - это список элементов какого-то класса, расширяющего IFoo. Поскольку тип T extends Container.IFoo & Container.IBar расширяет IFoo, список List - подходит, и его можно вернуть. Код: Container container = new Container<>(); List list = container.getIFooList(); IFoo foo = list.get( 0 ); скомпилируется, но сделать container.getIFooList().add( foo ); уже не получится. Использование типа ? extends IFoo не даст вызвать add с аргументом, отличным от null, и это хорошо, т.к. наш список на самом деле содержит объекты T extends Container.IFoo & Container.IBar, а не любых наследников IFoo: IFoo someFoo = new IFoo() { public void foo() {} }; container.getIFooList().add( someFoo ); IBar someBar = container.getIBarList().get( 0 ); Код мог бы вызвать ClassCastException во время выполнения, при присвоении someBar, но просто не скомпилируется. Конструктор нужно переписать, как public Container() { mList = new ArrayList(); } либо объявить private List mList;

четверг, 9 января 2020 г.

Особенности работы дженериков в Java

#java #ооп #generics


Прежде чем отвечать, прошу вдумчиво прочитать и осмыслить вопрос.
Итак у нас есть такой код, он скомпилируется и никаких ошибок не будет:

public static void main(String[] args) {
    method(4,"s");
}

static  void method(K k1, K k2) {

}


Я думал, что логически ошибка должна быть, ну раз нет, значит такие особенности джавы.
Далее, есть такой код:

public static void main(String[] args) {
    method(new ArrayList(), new Object());
}

static  void method(List k, K k2) {

}


Этот код уже выдает ошибку компиляции. Компилятор говорит, что я в method (List,
java.util.concurrent.Executor) засовываю не то, что надо. Действительно, Object это
вам не Executor. Но вот теперь главный мой вопрос почему в первом случае компилятору
пофиг, что одному и тому же параметризованному типу присваиваются разные типы данных,
а во втором случае компилятор на основе первого аргумента метода вычисляет какой должен
быть аргумент второго метода. Я не понимаю какая логика работает в обоих случаях.

И еще один не менее важный вопрос, если немного изменить второй пример кода вот так:

public static void main(String[] args) {
    method(new ArrayList(), 150);
}

static  void method(List k, K k2) {

}


Теперь никакой ошибки не возникает, причем мы можем пихать любые типы данных а компилятору
все равно! Мы всего лишь изменили List на List. Тут я уже даже теряюсь,
что это значит. Лист который содержит что-то, что наследует К, а что такое К? И почему
в итоге нам разрешено класть в параметры метода что угодно (напоминаю, что в предыдущем
примере с листом это не разрешено).
    


Ответы

Ответ 1



Границы для параметров параметризованных типов работают несколько иначе, нежели для простых типов. В первом случае оба типа могут быть приведены к Object, во втором случае тип может быть только либо Object, либо Executor. Если просто тип может быть легко "снижен" до объекта, то дженерики в параметризованных типах такое по умолчанию не позволяют - пока вы не укажете ? extends K, разрешающее использование наследников K, или ? super K, разрешающее использование предков K. В данном случае вы указали ? extends K, что позволяет использовать Object вместо K, таким образом список параметров метода формируется как List, Object. Могу быть в чем-то неправ, точные ответы на эти вопросы содержатся в JLS (Java Language Specification)