Custom List Generics Problem

Discussion in 'Plugin Development' started by PreFiXAUT, Dec 19, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    PreFiXAUT

    Hey everybody, I know this isn't really a Bukkit-releated Question, but I work on a custom List for my Projects (which includes Plugins) so I hope that's fine.

    Alright, so I have my List, which is a Class. This Class has a Generic with a Object which implements a custom Interface, right now it's like this: "public class DBList<T extends Parsable> implements List<T>".

    NOTE: Yes, it extends a Interface, because implementing a Interface is nearly the same as extending a abstract class, so that's how Java handles it *only for people who didn't know*

    So, my List is basicly able to load and save the saved Objects with the Methods from the Interface (Parsable). To access the Methods of the Interface/Object which inherits them, I need to have an Instance of T. And that's the Point. I have googled many hours now, but didn't come to a solution yet (maybe I'm just searching the wrong thing).

    I know that there's a "work around" which just simply put's in the Class<T> into the Constructor, but that's kinda stupid because you need to add it twice and it's basicly unnecessary. I tried
    Code:
    Class<T> clazZ = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    and other Reflection things, but they ONLY work if the T has not Interface or something like that :/

    I hope someone has a solution for this, eventho it's not especialy a Bukkit question :) Thanks in advance
     
  2. Offline

    fireblast709

    @PreFiXAUT do you have an example of what you are trying to do? (It doesn't have to be working code, as long as it shows your goals)
     
  3. Offline

    PreFiXAUT

    @fireblast709 Yea sure
    Code:
    public class DBList<T extends Parsable> implements List<T> {
    
      private File file;
      private T instance;
    
      public DBList(File file, Class<T> clazz) { // Using the class in the Constructor is the Objects-Class (T.class basicly). But I can't acceess T at all, since it's a Gerneric
        this.file = file;
        this.instance = clazz.newInstance();
      }
    
      public DBList(File file) { // File ONLY, getting the Class/Object of the Generic in some other way and save it
        this.file = file;
      }
    
    }
     
  4. Offline

    fireblast709

    @PreFiXAUT with the first constructor you could just pass an instance of T. The second, however, would serve no purpose. You would still need an instance of a possibly third type to convert the file to an instance of T.
     
  5. Offline

    PreFiXAUT

    @fireblast709 No it's not like this ... I just need to get an Instance of T out of the Generic in the Constructor. I just added the File because I need it in my real List.

    EDIT: Basicly get the Instance of T not by having a Parameter with a Class<T>, I would like to have it to be handled while it's in the Constructor-Block by getting the Instance from the Generic.

    How it works *who cares*: The Parsable Interface has 2 Methods -> "String toDataString()" and "Object parse()". I save the DataString from the Object with some extra Stuff into the File, and use the parse() Method of the same Object to get the Object again. I know there's Serializeable, but that doesn't work like I'ld like to, and it could loose a ton of Information.
     
    Last edited: Dec 19, 2014
  6. Offline

    fireblast709

    @PreFiXAUT why do you need to get the instance out of the generic type. Due to type erasure, the class's generic type will officially be Object. You can simply bypass your issue by adding f.e. a static create method:
    Code:java
    1. public class DBList<T extends Parsable> implements List<T>
    2. {
    3. // <Your stuff here>
    4.  
    5. public static DBList<SomeType> createSomeTypeDBList()
    6. {
    7. return new DBList<SomeType>(new SomeType());
    8. }
    9.  
    10. // Or even more generic: (feels like a bad pun tbh)
    11. public static <T extends Parsable> DBList<T> create(T instance)
    12. {
    13. return new DBList<T>(instance);
    14. }
    15. }
    You won't really gain anything from your attempt to use reflection, at most you will slow things down a bit :p.
     
    Last edited: Dec 19, 2014
  7. Offline

    PreFiXAUT

    @fireblast709 1. It is possible to get a Object/Instance/Class from a Generic using Reflection, because Java saves the Metadata of it and you can get this information with Reflection.
    2. Did you ever try to make static methods in Interfaces, which would be inherited by the Object? No? It doesn't work. That's why I need an instance, so I can use the instance to access the parse() Method to create/parse a new Object when I load the List from the File.
     
  8. Offline

    fireblast709

    Why? You gain nothing from using reflection in this case. You already give it a type, thus you already lost (a huge part of) your flexibility at that point. Might as well pass an instance to that class
    Interestingly enough, Java 8 allows it :p. For Java 7 compatibility (because that would be the first thing you would tell me about), I placed them in the DBList in this case, which is a class in your description.
    See my initial point. Your reflection seriously has no value in this use case.
     
  9. Offline

    xTrollxDudex

    @PreFiXAUT
    If your point of doing this is to have a serializable typed list, then use object schema and classcast to T....
     
  10. Offline

    PreFiXAUT

    @fireblast709 Wow....the parse Method wouldn't help at all. Is it really that hard to understand or am I messing up telling it propertly??

    Every Object has to handle their Stuff by their OWN. The static Methods would only work when I call Parsable#parse()/#toDataString(), but I can't tell how many Variables in a Object are relevant. I don't know if I can even reach them (maybe they are private and doesn't have a getter) and all that Stuff.

    The Object implements Parsable, and needs to provide a toDataString() which is responsible which Information is getting saved, and the parse() creates the Object again (which uses the exact same String which was given by the toDataString()).

    @xTrollxDudex Serializing doesn't really work, because it uses the toString() Method and some hacky Stuff. I want to have this clear and simple, provided by an Interface or whatever. But that's the best solution what I found yet.

    And Classcast to T -> There's my Situation again, I would like to have a Constructor without a Class in it which you need to provide, because you'ld need to duplicate it basicly
    Code:
    DBList<MyCoolObject> test = new DBList<MyCoolObject>(MyCoolObject.class);
    // Looks weird and you dupe it, that's why I'ld like to remove it.
     
  11. Offline

    fireblast709

    Allow me to summarize this once more: you are creating unnecessary overhead by being lazy. Generics don't exist for carrying metadata, they shouldn't be used in the way you are using them. Neither should you use reflection solely because you want the code to look fancy (in the end, people care about efficiency).

    The static methods were not there to be static, they were a short cut to object creation. If you want to let it be able to create new instances, use a factory or something alike
    Code:java
    1. new DBList<Object>(new Factory<Object>()
    2. {
    3. public Object newInstance()
    4. {
    5. return new Object();
    6. }
    7. });
    Then inside your DBList class
    Code:java
    1. public class DBList<T extends Parsable> implements List<T>
    2. {
    3. private final Factory<T> factory;
    4.  
    5. public DBList(Factory<T> factory)
    6. {
    7. this.factory = factory;
    8. // Note: no reflection used ;3
    9. T instance = this.factory.newInstance();
    10. }
    11. }
     
  12. Offline

    Totom3

    @PreFiXAUT To clarify the thing, generic types are LOST when your code is compiled. They do NOT exist anymore when your code is ran, and even Reflection will not help you get them back. It's not get the original types, not in this case at least. What's so problematic with @fireblast709 's first solution? It's literally the same as passing the instance of your JavaPlugin to your Listener class through the constructor.
     
  13. Offline

    Europia79

    Like @fireblast709 said, clazz.newInstance() is not good, and a factory is better.

    Let me give you an example to elaborate on WHY.

    I was using a framework where you hook in like this:
    Code:
    BattleArena.registerCompetition(this, "HostageArena", "vips", HostageArena.class, cmd);
    Now, the problem with this is that I cannot use dependency injection for HostageArena.class ...Dependency injection is where I write code for HostageArena.class and ask for object dependencies in the constructor.

    The solution ?
    Code:
    ArenaFactory factory = new HostageArenaFactory();
    BattleArena.registerCompetition(this, "HostageArena", "vips", factory, cmd);
    Now, under neath the hood, the factory can supply all the necessary dependencies for the HostageArena constructor... And the framework is oblivious to this detail... The framework doesn't care, it just calls newArena().

    Now, that was the first thing that caught my eye when reading your code. Next:
    Code:
    T instance;
    // ...
    this.instance = clazz.newInstance();
    
    The constructor should take the parameters and assign them to fields. If possible, try to avoid having the constructor do anything else. This is my source for further reading: http://misko.hevery.com/code-reviewers-guide/

    Take a look at this:
    Code:
    public class DBList<T extends Parsable> implements List<T> {
      private File file;
      private Parsable data;
      public DBList(File file, Parsable someData) { 
        this.file = file;
        this.data= someData;
      }
    }
    if T extends Parsable, why not replace type T with type Parsable ? And instead of asking for a Class in the constructor, why not ask for a Parsable instead ? What is wrong with asking for a Parsable in the constructor ?

    Hopefully you get this issue solved. Lemme know either way (i'm curious).
     
Thread Status:
Not open for further replies.

Share This Page