2 Replies - 2361 Views - Last Post: 19 April 2013 - 01:56 AM Rate Topic: -----

#1 socembopems   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 18-April 13

Maps and Polymorphism - ClassCastException

Posted 18 April 2013 - 05:01 PM

Ok, so my recent project is to create a system that has a database of exams and exam questions, along with the students and their answers. There are 3 types of questions (True/False, Mult. Choice, and Fill in the blank). I decided to use, for the list of exams and their questions, a Exam class as well as a Question interface that is implemented by 3 classes that each refer to the different types of questions. I'm using a HashMap to store this data, and I'm mapping each exam to its list of questions. My problem is, that when I attempt to add two different types of questions, it throws a ClassCastException. Here is the necessary code to see:

public class SystemManager implements Manager {
	HashMap<Exam, Set<Question>> examList = new HashMap<Exam, Set<Question>>();
	
	
	@Override
	public boolean addExam(int examId, String title) {
		Exam toAdd = new Exam(examId, title);
		Set<Exam> exSet = examList.keySet();
		if(exSet==null){
			examList.put(toAdd, null);
			return true;
		}else{
			for(Exam e : exSet){
				if(e.equals(toAdd)){
					return false;
				}
			}
		}
		examList.put(toAdd,null);
		return true;
	}

	private Exam getExamWithId(int examId){
		for(Exam e : examList.keySet()){
			if(e.getId()==examId){
				return e;
			}
		}
		return null;
	}

	@Override
	public void addTrueFalseQuestion(int examId, int questionNumber, String text, double points, boolean answer) {
		Set<Question> qs = examList.get(getExamWithId(examId));
		if(qs==null){
			qs = new TreeSet<Question>();
			examList.put(getExamWithId(examId), qs);
		}
		qs.add(new TrueFalseQuestion(questionNumber, text, points, answer));

	}

	@Override
	public void addMultipleChoiceQuestion(int examId, int questionNumber, String text, double points, String[] answer) {
		Set<Question> qs = examList.get(getExamWithId(examId));
		if(qs==null){
			qs = new TreeSet<Question>();
			examList.put(getExamWithId(examId), qs);
		}
		MultipleChoiceQuestion q = new MultipleChoiceQuestion(questionNumber, text, points, answer);
		qs.add(q);

	}

	@Override
	public void addFillInTheBlanksQuestion(int examId, int questionNumber, String text, double points, String[] answer) {
		Set<Question> qs = examList.get(getExamWithId(examId));
		if(qs==null){
			qs = new TreeSet<Question>();
			examList.put(getExamWithId(examId), qs);
		}
		qs.add(new FillInBlanksQuestion(questionNumber, text, points, answer));

	}



The point where I'm getting an error in the JUnit test:
int examId = 1;
		manager.addExam(examId, "Midterm #1");
		
		manager.addTrueFalseQuestion(examId, 1, "Java methods are examples of procedural abstractions.", 2, true);
		
		manager.addTrueFalseQuestion(examId, 2, "An inner class can only access public variables and methods of the enclosing class.", 2, false);
		
		String questionText = "Which of the following allow us to define an abstract class?\n";
		questionText += "A: abstract   B: equals   C: class   D: final ";
		double points = 4;
		manager.addMultipleChoiceQuestion(examId, 3, questionText, points, new String[]{"A"}); //error here



Thanks in advance guys, let me know if I need to clarify anything

Is This A Good Question/Topic? 0
  • +

Replies To: Maps and Polymorphism - ClassCastException

#2 ChrisGulddahl   User is offline

  • New D.I.C Head

Reputation: 4
  • View blog
  • Posts: 18
  • Joined: 17-April 13

Re: Maps and Polymorphism - ClassCastException

Posted 19 April 2013 - 12:25 AM

I can't spot any errors.
Can you show us your implementation of MultipleChoiceQuestion?

A side note: You shouldn't need to explicitly cast {"A"} to String[]. Unless I missed something..
Was This Post Helpful? 0
  • +
  • -

#3 cfoley   User is offline

  • Cabbage
  • member icon

Reputation: 2388
  • View blog
  • Posts: 5,013
  • Joined: 11-December 07

Re: Maps and Polymorphism - ClassCastException

Posted 19 April 2013 - 01:56 AM

The only thing I can see that might go wrong is converting the int to an Exam, although I would have expected a logic error rather than an exception. Can you post your stack trace?

By the way, you have a lot of code repetition in your various addQuestion methods. You should extract that out into a method. Then if the bug is in that bit, you only have to fix it once:

@Override
public void addMultipleChoiceQuestion(int examId, int questionNumber, String text, double points, String[] answer) {
    Question q = new MultipleChoiceQuestion(questionNumber, text, points, answer);
    addQuestion(examId, q);
}

private void addQuestion(int examId, Question q) {
    Set<Question> qs = examList.get(getExamWithId(examId));
    if(qs==null){
        qs = new TreeSet<Question>();
        examList.put(getExamWithId(examId), qs);
    }
    MultipleChoiceQuestion q = new MultipleChoiceQuestion(questionNumber, text, points, answer);
    qs.add(q);
}




I have to wonder about the relationships between your classes. If you have an Exam class, would it not be sensible for it to contain a list of questions? This would alleviate the need for a map from Exam to Question. Here is how I might approach the same problem:

class Exam {

	private int id;
	private String title;
	private List<Question> questions;

	public Exam(int id, String title) {
		this.id = id;
		this.title = title;
		questions = new ArrayList<>();
	}

	public int getID() {
		return id;
	}

	public void addQuestion(Question q) {
		question.add(q);
	}

	public List<Question> getQuestions() {
		return Collections.unmodifiableList(questions);
	}

	public boolean equals(Object other) {
		if (other instanceof Exam) {
			return false;
		} else {
			return equalsExam((Exam) other);
		}
	}

	private boolean equalsExam(Exam other) {
		if (id != other.id) return false;
		if (!title.equals(other.title)) return false;
		return true;
	}

	private int hashCode() {
		// http://en.wikipedia.org/wiki/Java_hashCode()#hashCode.28.29_in_general
		hash = 1;
		hash += 17 * id;
		hash += 37 * text.hashCode();
		return hash;
	}
}


public class SystemManager implements Manager {

	private Map<Integer, Exam> idToExam = new HashMap<Integer, Exam>();
	
	@Override
	public boolean addExam(int examId, String title) {
		Exam e = new Exam(examId, title);
		return exams.put(examId, e);
	}

	@Override
	public void addTrueFalseQuestion(int examId, int questionNumber, String text, double points, boolean answer) {
		Question q = new TrueFalseQuestion(questionNumber, text, points, answer);
		addQuestion(q);
	}

	public void addQuestion(int examId, Question q) {
		Exam e = getExam(examId);
		e.addQuestion(q);
	}

	private Exam getExam(int id){
		Exam result = idToExam.get(id);
		if (result == null) {
			throw new NoSuchElementException("No exam for id " + id);
		}
		return result;
	}

	@Override
	public void addMultipleChoiceQuestion(int examId, int questionNumber, String text, double points, String[] answer) {
		Question q = new MultipleChoiceQuestion(questionNumber, text, points, answer);
		addQuestion(q);
	}

	@Override
	public void addFillInTheBlanksQuestion(int examId, int questionNumber, String text, double points, String[] answer) {
		Question q = new FillInBlanksQuestion(questionNumber, text, points, answer));
		addQuestion(q);
	}

}


I'm not too keen on the different methods for adding various kinds of questions. What if you add more kinds of questions later? Whatever class calls the emthods on StsyemManager should have access to the constructors. I think that is where the Question objects should be created. If the constructors are not available to that class then consider writing a factory class that it does have access to. The SystemManager now becomes:

public class SystemManager implements Manager {

	private Map<Integer, Exam> idToExam = new HashMap<Integer, Exam>();
	
	@Override
	public boolean addExam(int examId, String title) {
		Exam e = new Exam(examId, title);
		return exams.put(examId, e);
	}

	public void addQuestion(int examId, Question q) {
		Exam e = getExam(examId);
		e.addQuestion(q);
	}

	private Exam getExam(int id){
		Exam result = idToExam.get(id);
		if (result == null) {
			throw new NoSuchElementException("No exam for id " + id);
		}
		return result;
	}

}

This post has been edited by cfoley: 19 April 2013 - 01:58 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1