1 Востаннє редагувалося Dopamine (20.04.2016 23:38:28)

Тема: Generic Інтерфейс і Клас

Побачив в книжці таку річ:
https://farm2.staticflickr.com/1629/25944338723_3043c3427f_o.png

Потім в інших джерелах таку, яка працює:

public class Address : IComparable<T>, IEquals<T>

В книжці застаріла інформація? Чому в ній говориться, що не можна не genec класом наслідувати  generic інтерфейс?

2

Re: Generic Інтерфейс і Клас

Не шарповець, але я бачу кілька варіантів, які make sense  і які б спрацювали у джаві:
class ByTwos<T> : ISeries<T>
class ByTwos : ISeries<SomeClassType>
З моєї точки зору, наведений вами приклад неправильний, але знову ж таки, шарп взагалі не знаю :)

Подякували: leofun011

3

Re: Generic Інтерфейс і Клас

я от шарп наче знаю, але дженерики ніколи не юзав. Якось побачив код знайомого, щось типу

void Method<T>(T obj)
{
    if(T == typeof(int))
    {
    }
    else if(T == typeof(float))
    {
    }
}

ну щось типу таке, і думаю - а нафіг воно мені тре? я можу просто перевантажити метод під необхідні типи аргументів, і все буде те ж саме і без купи if'ів

4

Re: Generic Інтерфейс і Клас

Ніколи не розумів, чому люди виносять такі питання на форум, а не намагаються перевірити в компіляторі: http://ideone.com/V3Aa9O
Помилкова інформація в інших джерелах (якщо, звісно, це весь важливий код, а не фрагмент - ми ж не телепати). Дженерік - це заготовка для класу чи функції, яка в певних умовах стає таким класом чи функцією із конкретним типом. Відповідно, наслідувати дженеріку без уточнення типу може тільки інший дженерік. Хоча якщо цей рядок -  елемент дженеріка... думайте самі далі, ви ж нам коду не дали.
Ну а використовувати без потреби рефлексії, навіть такі прості, мені таки видається ознакою неякісного коду. Рефлексії потрібні, коли є великий код, в який важко влізти, але там не передбачено якесь відображення типів (поганий поліморфізм, скажімо). Тут же всі типи очевидні - значить, if-и зайві.

Подякували: leofun01, FakiNyan2

5 Востаннє редагувалося iovchynnikov (21.04.2016 10:43:26)

Re: Generic Інтерфейс і Клас

FakiNyan написав:

я от шарп наче знаю, але дженерики ніколи не юзав. Якось побачив код знайомого, щось типу

void Method<T>(T obj)
{
if(T==typeof(int))
{
}
else if(T==typeof(float))
{
}
}

ну щось типу таке, і думаю - а нафіг воно мені тре? я можу просто перевантажити метод під необхідні типи аргументів, і все буде те ж саме і без купи if'ів

Перед тим як запитати "а нафіг воно треба", запитайте себе "а чи розробники майже усіх типізованих мов, імплементували б таку, не найпростішу, функціональність у своїх мовах просто так?" А відповідь на це питання треба шукати у книжках і документації, а не у кривому коді знайомих :)

Покажу приклад "нафіг".

Уявіть завдання: заімплементувати клас ArrayList списку на основі масиву. Клас має бути генеричним у тому сенсі, що 1 його імплементація може використовуватися для переховування різних типів значень, хоч і одного(!!!) типу.

Імплементація без дженеріків (java):

class ArrayList {
    private Object[] body;
    
    public void add(Object value) {
         // add value to this.body
    }
    
    public Object get(int index) {
         return body[index];
    }
}

А тепер про мінуси такої реалізації:
1) Необхідне зведення типів. Якщо навіть у вас ліст містить лише Integer, то усе одно вам треба зводити типи, бо get повертає Object:

ArrayList list = new ArrayList();
list.add(15);

int firstElement = (Integer) list.get(0);

2) Невизначеність фактичного типу зберігаємих елементів. Іншими словами, так як у нас імплементація на основі масиву Object[], ми можемо у нього пхати що завгодно і те, що там лежить, відомо лише під час runtime.

ArrayList list = new ArrayList();
list.add(15); // OK
list.add(new Home()); // теж ОК
list.add("asdadada"); // теж ОК

І під час list.get() ви насправді не знаєте який там тип і з'являються бридкі конструкції на кшталт:

Object rawElement = list.get(0);
if (rawElement instanceof String) {
    //
} else if (rawElement instanceof Integer) {
    //
}

+ якщо слідкувати вимозі, що ліст може містити лише дані 1 типу, треба робити перевірки під час add(). До того ж треба визначити яким саме чином визначати той головний тип з яким порівнювати, під час тієї перевірки (на приклад передавати у конструктор клас new ArrayList(Integer.class) і з ним у add перевіряти)

Імплементація з дженеріками (java):

class ArrayList<T> {
    private T[] body;
    
    public void add(T value) {
         // add value to this.body
    }
    
    public T get(int index) {
         return body[index];
    }
}

Такий код виправляє всі проблеми попереднього підходу:
1) Тип визначений під час компіляції та ним й перевіряється. Використовуючи такий код, ви самостійно вказуєте тип і тим самим змушуєте компілятор перевіряти типи що там знаходяться.

ArrayList<Integer> list = new ArrayList<>();
list.add(15); // OK
list.add(new Home()); // compile time error, Home is NOT instanceof Integer
list.add("asdadada"); // compile time error, String is NOT instanceof Integer

Жодних перевірок instanceof.
2) Так як тип визначений під час компіляції, жодних додаткових зведень не вимагається.

ArrayList<Integer> list = new ArrayList();
list.add(15);

int firstElement = list.get(0); // компілятор вже на цій стадії знає що там лише int

Прихований текст

В джаві 2га імплементація зводиться до 1ї компілятором, тобто фактично байт-код містить Object[] body; а не T[] body. Детальніше https://docs.oracle.com/javase/tutorial … asure.html

Подякували: leofun01, FakiNyan2

6 Востаннє редагувалося FakiNyan (21.04.2016 13:47:37)

Re: Generic Інтерфейс і Клас

koala написав:

Ніколи не розумів, чому люди виносять такі питання на форум, а не намагаються перевірити в компіляторі:

Прихований текст

тому що ліньки, можна запитати на форумі і з думкою - "я зробив все, що зміг!" дивитись смішні відюшки на ютубі

Подякували: leofun011

7

Re: Generic Інтерфейс і Клас

Ось вам приклад використання дженеріків для універсального класу, що серіалізує та десеріалує обєкти в XML файл. Без дженеріків тут було би сумно

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Common.AppLog;
using System.Xml.Serialization;
using System.Xml;

namespace Common.Services
{
    public class XMLDataContext<T> where T: class
    {
        private string m_FileName;

        public XMLDataContext(string _fileName)
        {
            m_FileName = _fileName;
        }

        private string LoadFile()
        {
            return FSmanager.LoadFile(m_FileName);
        }

        private bool SaveFile(string _Fstring)
        {
            return FSmanager.CreateFile(m_FileName,_Fstring);
        }

        private bool SaveFile(byte[] _buffer)
        {
            return FSmanager.SavetoBinFile(m_FileName, _buffer);
        }

        private byte[] SerializeToXML(T _obj, Encoding _encoding = null)
        {
            XmlSerializer _ser = new XmlSerializer(typeof(T));
            byte[] _buffer = new byte[] {};
            using (MemoryStream _stream = new MemoryStream())
            {
                _encoding = _encoding == null ? Encoding.UTF8 : _encoding;
                using (StreamWriter _sw = new StreamWriter(_stream, _encoding))
                    _ser.Serialize(_sw, _obj);
                _buffer = _stream.ToArray();
            }
            return _buffer;
        }

        private T DeserializeFromXML(string _xml)
        {
            object _obj=null;
            XmlSerializer _ser = new XmlSerializer(typeof(T));
            try
            {
                using (TextReader _tr = new StringReader(_xml))
                    _obj = _ser.Deserialize(_tr);
            }
            catch (Exception _ex)
            {
                SysLog.SaveToLog("DataContext.DeserializeFromXML (file name - <" + m_FileName + ">):\n   " + _ex.Message);
            }
            return (T)_obj;
        }

        public T GetObject()
        {
            string _xmlFromFile=LoadFile();
            T _retObj;
            
            if (_xmlFromFile != string.Empty)
                _retObj=DeserializeFromXML(_xmlFromFile);
            else _retObj=null;

            return _retObj;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_obj">Object for save into the XML file</param>
        /// <param name="_encoding">System.Text.Encoding object, default encoding is UTF8 if parameter is null</param>
        /// <returns>True if object successfully saved to file</returns>
        public bool SaveObject(T _obj, Encoding _encoding=null)
        {
            bool _ret = false;
            int i = 0;
            byte[] _xmlBuffer = SerializeToXML(_obj, _encoding);
            if ( _xmlBuffer.Length> 0 )
                while (!_ret && i < 10) // 10 attempts to save into the file
                {
                    i++;
                    _ret = SaveFile(_xmlBuffer);
                }
            return _ret;
        }
    }
}
Подякували: leofun011