8 Replies - 423 Views - Last Post: 30 April 2012 - 08:30 PM Rate Topic: -----

#1 giggly kisses  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 80
  • View blog
  • Posts: 391
  • Joined: 29-March 09

Generics Wildcard

Posted 29 April 2012 - 07:01 PM

I'm currently working on an installer API and making use of generics (which seem to always blow my mind). Right now I have an AbstractInstaller class, which will be the base class of all installers used with the API. I also made an AbstractState class which will be the base class of all the states used by the installer objects. The AbstractState class contains a 'label' value, and a key int value. The AbstractInstaller class uses generics to declare the type in a stack used by the class; the type has to extend the AbstractState class. The AbstractState class also makes use of generics by setting the type of the object that is used for the label object.

Now for the problem. Right now I have a method inside the AbstractInstaller class that is supposed to return a new reference to the currentState object. At the moment it returns the reference and not a new object, which is a potential security risk. To fix this I tried to do something like this:

public T getCurrentState() {
    return new T(this.currentState);
}



But I get an error stating "cannot instantiate the type T". Not quite sure where to go from here. Anyone have any idea?

Here is the rest of my code:

AbstractInstaller:
package com.installer.core;

import java.util.*;
import java.io.*;

/* A class meant to be the super class of all installer objects
 * used with the Installer API.
 */
public abstract class AbstractInstaller<T extends AbstractState<?>> {
	private File installDirectory;
	private Stack<T> stateStack, backStack;
	private T currentState;
	
	/* Constructor that takes a Stack of type T and a File that points
	 * to the desired install directory.
	 */
	public AbstractInstaller(Stack<T> states, File installDir) {
		this.stateStack = states;
		this.installDirectory = installDir;
		this.currentState = stateStack.pop();
		this.backStack = new Stack<T>();
	}
	
	/* Constructor that takes a Vector of type T and a File that points
	 * to the desired install directory.
	 */
	public AbstractInstaller(Vector<T> states, File installDir) {
		this.stateStack = new Stack<T>();
		for(int i=states.size()-1; i>0; i--) {
			stateStack.push(states.get(i));
		}
		
		this.installDirectory = installDir;
		this.currentState = stateStack.pop();
		this.backStack = new Stack<T>();
	}
	
	/* Sets the installDirectory parameter to the value passed in 
	 * by installDir
	 */
	public void setInstallDirectroy(File installDir) {
		this.installDirectory = installDir;
	}
	
	/* Returns a new File object with pointing to the directory 
	 * in installDirectory.
	 */
	public File getInstallDirectory() {
		return new File(this.installDirectory.getPath());
	}
	
	/* Adds the current value of currentState to the backStack,
	 * adds the next value from stateStack to currentState and
	 * returns the value in currentValue. Throws an IndexOutOfBounds
	 * exception if stateStack is empty. 
	 */
	public T goForward() throws IndexOutOfBoundsException {
		try {
			stateStack.peek();
			backStack.push(currentState);
			currentState = stateStack.pop();
		}
		catch(EmptyStackException ex) {
			throw new IndexOutOfBoundsException("Can't go forward");
		}
		
		return currentState;
	}
	
	/* Adds the current value of currentState to the stateStack,
	 * adds the next value from backStack to currentState and
	 * returns the value in currentValue. Throws an IndexOutOfBounds
	 * exception if backStack is empty. 
	 */
	public T goBackward() throws IndexOutOfBoundsException {
		try {
			backStack.peek();
			stateStack.push(currentState);
			currentState = backStack.pop();
		}
		catch(EmptyStackException ex) {
			throw new IndexOutOfBoundsException("Can't go backward");
		}
		
		return currentState;
	}
	
	/*Returns a reference to the object in the current state.
	 */
	public T getCurrentState() {
		return new T(this.currentState);
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return "State Stack: " + stateStack.toString() + "\nBack Stack: " + backStack.toString() + 
		"\nCurrent State: " + getCurrentState() + "\nInstall Directory: " + installDirectory.getPath();
	}
}




AbstractState:
package com.installer.core;

public abstract class AbstractState<T> {
	private T label;
	private int key;
	
	public AbstractState(T label, int key) {
		this.label = label;
		this.key = key;
	}
	
	public AbstractState(AbstractState<T> state) {
		this(state.getLabel(), state.getKey());
	}
	
	public T getLabel() {
		return this.label;
	}
	
	public int getKey() {
		return this.key;
	}
	
	public String toString() {
		return "[" + key + ", " + label.toString() + "]";
	}
}




Edit: Just noticed that I forgot to change the title once I realized the wildcard had nothing to do with the problem. Would change it but I already submitted. Sorry for the misleading title. :whistling:

This post has been edited by giggly kisses: 29 April 2012 - 07:02 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Generics Wildcard

#2 ianian112  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 119
  • View blog
  • Posts: 378
  • Joined: 28-November 09

Re: Generics Wildcard

Posted 29 April 2012 - 08:04 PM

return new T(this.currentState);


makes no sense when you think about it with generics. What if Generic T has no constructor that accepts itself at compile time?

For what you want my initial guess would use the .clone() method in object to create a copy, or you could define an abstract method that returns T in AbstractState that forces the implementing class to define a method that returns a deep copy
Was This Post Helpful? 1
  • +
  • -

#3 giggly kisses  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 80
  • View blog
  • Posts: 391
  • Joined: 29-March 09

Re: Generics Wildcard

Posted 29 April 2012 - 08:12 PM

View Postianian112, on 29 April 2012 - 09:04 PM, said:

return new T(this.currentState);


makes no sense when you think about it with generics. What if Generic T has no constructor that accepts itself at compile time?


I thought of that, which is why I used <T extends AbstractState>, but thinking this was going to make a difference was wishful thinking. :P

Quote

For what you want my initial guess would use the .clone() method in object to create a copy, or you could define an abstract method that returns T in AbstractState that forces the implementing class to define a method that returns a deep copy


I was thinking about using the clone method, but was hoping there was a way to solve it with a copy constructor (I've read a lot that clone is frowned upon). Your second suggestion is one I haven't thought of and is a really good idea. I'll try implementing this now, thanks. :)
Was This Post Helpful? 0
  • +
  • -

#4 blackcompe  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1150
  • View blog
  • Posts: 2,528
  • Joined: 05-May 05

Re: Generics Wildcard

Posted 29 April 2012 - 08:48 PM

Quote

At the moment it returns the reference and not a new object, which is a potential security risk. To fix this I tried to do something like this:


You could use a copy constructor or simply implement the Cloneable interface to return a deep copy.

Quote

But I get an error stating "cannot instantiate the type T".


This is due to type erasure. Basically, all the T's in the class are erased (and replaced with Object ?) at compile-time. You can't instantiate something that doesn't exist.

import java.util.*;
import java.lang.*;
import java.lang.reflect.*;
 
class Main
{
        public static void main (String[] args) throws java.lang.Exception
        {
                StartState ss = new StartState("Hello World");
                AbstractState as = newInstance(ss);
                System.out.println(ss);
                System.out.println(as);
                System.out.println("ss != as : "+(as != null && ss != as));
        }
 
        private static abstract class AbstractState{}
        private static class StartState extends AbstractState{
                private String label;
                public StartState(String label){ this.label = label; }
                public StartState(StartState ss){ this.label = ss.label; }
                public String toString(){return label;}
        }
 
        private static <T extends AbstractState> T newInstance(T obj) throws Exception {
                Class<?> cls = ((AbstractState)obj).getClass();
                Constructor<?> con = cls.getConstructor(cls);
                return (T)con.newInstance(obj);
        }
}



newInstance is an analogue of getCurrentState.
Was This Post Helpful? 1
  • +
  • -

#5 giggly kisses  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 80
  • View blog
  • Posts: 391
  • Joined: 29-March 09

Re: Generics Wildcard

Posted 29 April 2012 - 10:02 PM

Thanks blackcompe for the suggestion, didn't think of using reflection to solve this problem. Unfortunately you're method creates an unchecked cast warning in Eclipse (I've been spending about an hour trying to find a solution that wouldn't produce this warning, sadly I've been unsuccessful). I have come up with a 'solution' that doesn't involve reflection, although I'm not sure if it's any better (or worse). The main reason I'm going with the following solution (unless I can find one that doesn't produce the previous warning) is because it allows for an easy override implementation for anyone extending the AbstractState class. Here is my 'solution':

AbstractState:
package com.installer.core;

public abstract class AbstractState<T> {
	private T label;
	private int key;
	
	public AbstractState(T label, int key) {
		this.label = label;
		this.key = key;
	}
	
	public AbstractState(AbstractState<T> state) {
		this(state.getLabel(), state.getKey());
	}
	
	public T getLabel() {
		return this.label;
	}
	
	public int getKey() {
		return this.key;
	}
	
	public abstract AbstractState<T> getNewInstance();
	
	public String toString() {
		return "[" + key + ", " + label.toString() + "]";
	}
}



AbstractInstaller(Where the warning is at):
package com.installer.core;

import java.util.*;
import java.io.*;

/* A class meant to be the super class of all installer objects
 * used with the Installer API.
 */
public abstract class AbstractInstaller<T extends AbstractState<?>> {
	private File installDirectory;
	private Stack<T> stateStack, backStack;
	private T currentState;
	
	/* Constructor that takes a Stack of type T and a File that points
	 * to the desired install directory.
	 */
	public AbstractInstaller(Stack<T> states, File installDir) {
		this.stateStack = states;
		this.installDirectory = installDir;
		this.currentState = stateStack.pop();
		this.backStack = new Stack<T>();
	}
	
	/* Constructor that takes a Vector of type T and a File that points
	 * to the desired install directory.
	 */
	public AbstractInstaller(Vector<T> states, File installDir) {
		this.stateStack = new Stack<T>();
		for(int i=states.size()-1; i>0; i--) {
			stateStack.push(states.get(i));
		}
		
		this.installDirectory = installDir;
		this.currentState = stateStack.pop();
		this.backStack = new Stack<T>();
	}
	
	/* Sets the installDirectory parameter to the value passed in 
	 * by installDir
	 */
	public void setInstallDirectroy(File installDir) {
		this.installDirectory = installDir;
	}
	
	/* Returns a new File object with pointing to the directory 
	 * in installDirectory.
	 */
	public File getInstallDirectory() {
		return new File(this.installDirectory.getPath());
	}
	
	/* Adds the current value of currentState to the backStack,
	 * adds the next value from stateStack to currentState and
	 * returns the value in currentValue. Throws an IndexOutOfBounds
	 * exception if stateStack is empty. 
	 */
	public T goForward() throws IndexOutOfBoundsException {
		try {
			stateStack.peek();
			backStack.push(currentState);
			currentState = stateStack.pop();
		}
		catch(EmptyStackException ex) {
			throw new IndexOutOfBoundsException("Can't go forward");
		}
		
		return currentState;
	}
	
	/* Adds the current value of currentState to the stateStack,
	 * adds the next value from backStack to currentState and
	 * returns the value in currentValue. Throws an IndexOutOfBounds
	 * exception if backStack is empty. 
	 */
	public T goBackward() throws IndexOutOfBoundsException {
		try {
			backStack.peek();
			stateStack.push(currentState);
			currentState = backStack.pop();
		}
		catch(EmptyStackException ex) {
			throw new IndexOutOfBoundsException("Can't go backward");
		}
		
		return currentState;
	}
	
	/*Unchecked cast warning is here. ://>
	 */
	public T getCurrentState() {
		return (T) currentState.getNewInstance();
	}
	
	public T getState() {
		return currentState;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return "State Stack: " + stateStack.toString() + "\nBack Stack: " + backStack.toString() + 
		"\nCurrent State: " + getCurrentState() + "\nInstall Directory: " + installDirectory.getPath();
	}
}



I also wrote some test classes.

TestState:
package Tests;

import com.installer.core.*;

public class TestState extends AbstractState<String> {
	
	public TestState(String label, int key) {
		super(label, key);
	}
	
	public TestState(TestState instance) {
		super(instance);
	}

	//Allows for user of API to easily implement function.
	public TestState getNewInstance() {
		return new TestState(this);
	}
}



TestInstaller:
package Tests;

import java.io.*;
import java.util.*;
import com.installer.core.*;

public class TestInstaller extends AbstractInstaller<TestState> {

	public TestInstaller(Stack<TestState> states, File installDir) {
		super(states, installDir);
	}
	
	public TestInstaller(Vector<TestState> states, File installDir) {
		super(states, installDir);
	}
}


Was This Post Helpful? 0
  • +
  • -

#6 blackcompe  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1150
  • View blog
  • Posts: 2,528
  • Joined: 05-May 05

Re: Generics Wildcard

Posted 29 April 2012 - 11:18 PM

Cool. I actually like your design. Didn't even think about that.
Was This Post Helpful? 0
  • +
  • -

#7 giggly kisses  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 80
  • View blog
  • Posts: 391
  • Joined: 29-March 09

Re: Generics Wildcard

Posted 30 April 2012 - 12:21 PM

Hey, thanks! Sadly it still produces the unchecked cast warning. :( Thanks again for your post, it shed some light on what reflection can be used for :) (still a fairly new topic to me, my understanding of it is still very abstract).
Was This Post Helpful? 0
  • +
  • -

#8 blackcompe  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1150
  • View blog
  • Posts: 2,528
  • Joined: 05-May 05

Re: Generics Wildcard

Posted 30 April 2012 - 07:10 PM

You do know you can use @SuppressWarnings(value = "unchecked") to suppress those, right?
Was This Post Helpful? 0
  • +
  • -

#9 giggly kisses  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 80
  • View blog
  • Posts: 391
  • Joined: 29-March 09

Re: Generics Wildcard

Posted 30 April 2012 - 08:30 PM

Yup, I'm using it right now. I was just being anal about my code. :P
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1