albertlee Posted July 27, 2006 Posted July 27, 2006 As opposed to my "trivial java questions" thread, I will post all my in the near future Java questions which I think concern more to my understanding of Java programming language and computer science rather than just facts. So, I think it's better to seperate them. This is the head start question: 1) public class Foo{ public int a; public Foo(){a = 3;} public void add(){a+=5;} } public class Bar extends Foo{ public int a; public Bar(){a = 8;} public vid add(){a+=5;} } why does this code: Foo foo = new Bar(); foo.add(); [b]System.out.print(foo.a);[/b] show the result 3 instead of the expected 13? thanks
albertlee Posted July 27, 2006 Author Posted July 27, 2006 another Java question: 2) import java.util.*; public class Test{ static void d(List<? [b]super[/b] String> d){d.add("bla"); d.add("dsfew");} public static void main(String[] ag){ List l = new ArrayList(); d(l); Iterator i = l.iterator(); System.out.println(i.next()); System.out.println(i.next()); } } As simple as this, why does the bold part, if "super", produces no compilation error, while "extends" prodcues such error at 'add' method? to my expectation, both should yield compilation error because of the question mark, which indicates anything, so canot add any element to the list..... please help thanks
Aeternus Posted July 27, 2006 Posted July 27, 2006 [Referring to the first question] How are you getting that to compile? Firstly Foo.add(), Bar.add() and Bar.Foo() need return types (otherwise the compiler errors on (1.4 and 1.5)). Secondly, Foo and Bar are not compatible types :\ Am I missing something? Are you using a different compiler to me?
albertlee Posted July 27, 2006 Author Posted July 27, 2006 sorry, Aeternus, I have edited the post....the rest of my question remains constant please help thanks
albertlee Posted July 27, 2006 Author Posted July 27, 2006 3) import java.util.*; public class Test{ static public void main(String[] eargqe){ PriorityQueue<String> pq = new PriorityQueue<String>(); pq.add("a");pq.add("b");pq.add("c"); System.out.println(pq.poll()+" : "+pq.peek()); } } why isn't the output "a : b" but "b : c"? first, the poll method retrieves a and then removes it, then peek method retrieves b, simple as that, but why is this right? please help
Aeternus Posted July 27, 2006 Posted July 27, 2006 I still get errors about no return type for Bar.Foo() and even if I comment that out, printing foo simply gives me the object reference "Bar@10d448" (as you would expect as the toString method hasn't been overridden).
albertlee Posted July 27, 2006 Author Posted July 27, 2006 4) public static void main(String[] ag){ try{ File f = new File("f"); File g = new File(f, "g"); if(!f.exists()){ fw.createNewFile(); } if(!g.exists()){ g.createNewFile();} } catch(IOException ioe){System.out.println(ioe);} } I dont get it, why an IOException is fired at runtime from the createNewFile method? the exception said that the system cannot find the path specified... well, shouldn't that be the point of the createNewFile method? please help
albertlee Posted July 27, 2006 Author Posted July 27, 2006 oh...Aeternus, sorry for my mistake again.... the code has been changed again....
Aeternus Posted July 27, 2006 Posted July 27, 2006 Ok with regard to (1), I think this is because you are redeclaring "a" in Bar but are typecasting it to Foo, and therefore accessing the "a" that was declared in Foo, not that which has been declared an altered by the methods from Bar. This is rather hard to explain but if you simply take out "public int a;" from the Bar class, this should fix it. Another fix which might make things more clear is changing the Bar class to - public class Bar extends Foo{ public int a; public Bar(){super.a = 8;} public void add(){super.a+=5;} } This ensures that the a declared in the super class (Foo) is set to these values and not the new "a" declared in Bar.
albertlee Posted July 27, 2006 Author Posted July 27, 2006 5) import java.util.*; class km{ public int i; public km(int i){this.i=i;} public boolean equals(Object o){return i==((km)o).i;} public int hashCode(){return i;} } public class MapIt{ public static void main(String[] rafre){ Set<km> set = new HashSet<km>(); km k1 = new km(1); km k2 = new km(2); set.add(k1);set.add(k1); set.add(k2);set.add(k2); System.out.print(set.size() +":"); k2.i=1; System.out.print(set.size() +":"); set.remove(k1); System.out.print(set.size() +":"); set.remove(k2); System.out.print(set.size()); } } the result shows : 2:2:1:1 my expectation is 2:1:0:0, because first, there are 2 unique elements in the set. Second, the i value in k2 has been changed to 1, and since according to the km's equal method, now k2 and k1 are now same object, so there should be 1 element left. Third and Forth, the element has been removed, so 0 element left....This is my reasoning. please help thanks
albertlee Posted July 27, 2006 Author Posted July 27, 2006 Aeternus, thanks for your post regarding question 1. however it's still confusing. because the type of an object only indicates what data members and methods SHOULD it have, but the actual implementation/content of the data/methods should depend on the actual INSTANCE of the object. please help thanks
Aeternus Posted July 27, 2006 Posted July 27, 2006 I think the reason for (2) is that - List<? extends Object> paramName implies that that parameter can take any list that can hold any type of Object or of any type of subclass of Object. Say for instance you did - ... // Some Method public void doit(List<? extends Object> d) { d.add(new Object()); } ... List<String> list = new ArrayList<String>(); doit(list); ... Obviously I'm missing alot of code but I hope you get the idea. Now doit() takes in any List that can hold any subtype of Object or Object itself. So I've passed in my List that holds String's, it doesn't make sense for me to now add an Object to a list that holds String s as the base class that this list holds is String, not Object. Ie - List<String> x = new ArrayList<String>(); x.add(new Object()); won't work, and therefore, I think that is the same reason as why the code errors. This is all a bit silly with String as String is final anyway and so nothing can actually extend String but I'm guessing it isn't going as far as to see what actually IS being passed to the method but only what could be and should be legal? The reason it works with super is that if the list stores any class/type that is a super type of the class specified then an object of the class you specified can be stored in any of those lists because it is extended from that class. Ie - List<Object> x = new ArrayList<Object>(); x.add(new String()); works because you can do Object x = new String(); because String is a subclass of Object, and Object is super type of String. Not sure if this makes sense but that's how it seems things are working to me.
Aeternus Posted July 27, 2006 Posted July 27, 2006 Aeternus' date=' thanks for your post regarding question 1. however it's still confusing. because the type of an object only indicates what data members and methods SHOULD it have, but the actual implementation/content of the data/methods should depend on the actual INSTANCE of the object. please help thanks[/quote'] It still has both those members, it is just how they are accessed. By casting it to the Foo class/type you are saying that you wish to access things with the interface/class structure present in the Foo class, which indicates that you wish to access "a" as if it were from Foo. Ie there are 2 "a"s, FooInstance.a, BarInstance.a, depends on the context either one could be accessed. By casting to Foo you are saying only use things that would be present in Foo. Since BarInstance.a is declared by Bar, not by Foo, it can't be accessed when you have type cast the object to Foo as it isn't declared in the Foo class. The thing is, methods are overridden, fields aren't. It may seem strange that it doesn't actually change FooInstance.a instead of BarInstance.a when you run add() etc, but I'm sure you are aware of the fact that when you run overridden methods when you have typecast to a super class that you end up running the methods declared in the original class. IE when you run add() it runs Bar.add() and Bar.add() uses "a" as declared in Bar, ie BarInstance.a. I'm sorry if this is a little jumbled and probably wrong in some places, it's a little hard to explain what I mean but you can see it happening if you play about with using super.a, and a and so on. The main thing to notice is that you aren't overriding "a" when you declare it again in Bar, you are declaring an entirely seperate field that is used in preference to the original unless you explicitly specify. This is very much like how you can have a parameter to a method with the same name as a field of that class but still access both by using the this.? notation to access the field. The same is happening with this situation, you have the seperate field "a" declared in Bar, which you are accessing by using "a" but you can access the other field declared in Foo by using "super.a" to get at it. The reason that it prints out "super.a" when you are printing it out is that it, as you have typecast it to Foo, you are only accessing things that are present in the Foo class, which immediately means that the problem of which variable is which goes away, the only one available when you are restricting things to those declared in the Foo class is the seperate field "a" in Foo, which hasn't been changed by Bar.
Aeternus Posted July 27, 2006 Posted July 27, 2006 With regard to (2), I'm a little confused by the question. It outputs "a:b"... what is the problem with that? The PriorityQueue will order things according to their natural order, so it will put "a" at the head, then "b", then "c". You are printing pq.poll(), which grabs the head, removes it from the queue and returns it. So in this case, it removes "a" and returns "a". Then you are peeking at the head of the priority queue which is now "b", therefore you have "a" + ":" + "b" which is "a:b" which is what it prints out :\ Am I misunderstanding you or something? Did it print "b:c" for you? Have I misunderstood something?
Aeternus Posted July 27, 2006 Posted July 27, 2006 The reason you are getting an IOException in (4) is because you are calling g.createNewFile(), but you passed f as the parent to g, which implies that f is a directory, but you are creating f as a new file not a new directory, and so when you tried to create g, it throws an IOException as it can't treat a file (f) as a directory and so it can't create g inside f. To get this to work you need to do - import java.io.*; public class Files { public static void main(String[] ag){ try{ File f = new File("f"); File g = new File(f, "g"); if(!f.exists()){ f.mkdir(); } if(!g.exists()){ g.createNewFile(); } } catch(IOException ioe) { ioe.printStackTrace(); //System.out.println(ioe); } } } You'll notice I used f.mkdir() to create f as a directory and not as a file. You'll also notice that I did ioe.printStackTrace() instead of just printing ioe, which can make it a little easier to track things down sometimes.
Aeternus Posted July 27, 2006 Posted July 27, 2006 I think (5) is a simple misunderstanding of what is going on. You pass it an object, it uses the hash from object.hashCode() (which you overrided) to determine a hash key to use to put it in the HashMap uses by HashSet to store things. The only time it checks for duplication is when you try to add something new. The way it seems to do this is simply by checking for collisions in the hash map. The idea being that if they have the same key they will try to add to the same position in the Hashtable. When this collision is detected (ie you look at the position in the hashtable, it is already occupied, there has been a collision), it will then check to make sure this hasn't simply been a random collision (ie because of the hash function, 2 different objects might collide anyway) by using equals to test if they are equal, if they are equal then the add() will simply fail, if they aren't then some form of collision resolution will be used and the new entry will be placed somewhere sensible. The problem here is that if the hash key/code isn't the same for the new object being put in, then it can't be the same as another object because they probably won't match to the same position in the HashMap / HashTable and so won't have a chance to be compared with equal(). Sorry if this sounds a little confusing, perhaps the best way to explain this is to just go through each instance of the set.size() calls and explain what is happening at each - // The First one set.add(k1);set.add(k1); set.add(k2);set.add(k2); System.out.print(set.size() +":"); k1 returns the hashcode k1, and k1.equals(k1) so the second set.add(k1) will collide with the entry in the HashMap for the first and will be equal to it and so the second add will fail. The same is true with the second set.add(k2). Therefore we have 2 elements in the HashSet, so set.size() returns 2. // Second k2.i=1; System.out.print(set.size() +":"); Here, the statement, k2.i = 1; has made k1 equal to k2, however, the HashSet won't do anything about it as it only checks for duplication when you add the objects, it doesn't try to maintain this uniqueness whenever you change other things (how could it? none of it's methods are being run right now?). Therefore, the HashSet still has 2 elements and they are still at different positions (k2 doesn't move simply because it's hashcode has changed, that code was used to place it in the first place, but changing that code doesn't change the fact that it was placed in the table at the original hash code value). Therefore, as there are still 2 elements and no checks and so on have been done, set.size() will return 2. set.remove(k1); System.out.print(set.size() +":"); set.remove(k1), uses the k1.hashCode() to find the position of k1 in the hashtable, k1 equals() the element at that position, so it removes that element from the table. It doesn't go off searching for other elements that might equal k1 because it assumes that if they did, they would have been discarded when they were add(). Now there is only 1 element in the HashSet left which wasn't removed because duplication avoidance is handled by add(). Therefore, as there is 1 element, set.size() returns 1. // Four set.remove(k2); System.out.print(set.size()); This is the funky bit, as k2.i has now changed to 1 (it was originally 2), k2.hashCode() will return 2, and HashSet will use that to look for it, but there is nothing at the position because that was the position taken by k1, k2 was originally placed into whatever place is specified by "2" not "1". Therefore as it can't find k2, it simply fails. k2 has not been removed, it is still at "2", it hasn't moved, it is simply that the hashCode() has changed and so HashSet tries to look for it in a different place. Since there is still 1 element in the HashSet (k2), set.size() returns 1. Therefore it prints 2:2:1:1 You can further reinforce alot of this by either making hashCode() return a constant or by making it return a different value every time (one way of doing this is to have a static field that is incremented every time hashCode() is called) and then using the iterator() of HashSet to see what is going on inside. Sorry if some of this seems a little rushed or lacks some explanation, I have to go off up town now so I'll be back later to try and clear things up.
albertlee Posted July 27, 2006 Author Posted July 27, 2006 I still have problem with question 1... ok, I understand that there is the typecasting for the data member, therefore, the actual "i" being accessed is the one from Foo. However, can you really tell me, the reason why this is designed this way? I mean, why data member are typcasted while methods are overriden instead of typecasted. Why not make this issue consistent? , since we can access both of them with super keyword any way please help thx
albertlee Posted July 27, 2006 Author Posted July 27, 2006 For question 2, Aeternus, I honestly have to say, you dont quite understand the concept of generics in Java fully. Well, simply, List<Object> l = new ArrayList<Object>(); l.add(new String("test"); wont compile because in Java's generics, a generic collection can basically only accepts 1 type of elements. And clearly, String class is not Object class, even though String is a subclass of Object. Try and compile it and you will see it wont work..... any way, really thanks for your help on this, but since you are majoring computer science, I think you can really give more information on question 2 again... please help thanks
Aeternus Posted July 27, 2006 Posted July 27, 2006 Umm... I think you are wrong. It compiles fine for me. The reason you might be getting an error is because you are missing the final ). import java.util.*; public class Test{ public static void main(String[] ag){ List<Object> l = new ArrayList<Object>(); l.add(new String("cheese")); l.add(new Object()); l.add(new Integer(5)); Iterator i = l.iterator(); while (i.hasNext()) { System.out.println(i.next()); } } } compiles fine and when run produces the output - cheese java.lang.Object@2e7263 5 What compiler error are you getting?
albertlee Posted July 27, 2006 Author Posted July 27, 2006 Secondly, for question 1.... class a{ [b]//void meth() throws Exception{throw new Exception();}[/b] void meth() {System.out.println("hello from a");} } class b extends a{ void meth() {System.out.println("hello from b");} } public class Test{ public static void main(String[] ag){ a o = new b(); [b]o.meth();[/b] } } how come the bold method, when uncomented, will be typecasted, while the other one underneath, wont?? This is confusing.... :-? plz help
albertlee Posted July 27, 2006 Author Posted July 27, 2006 I also dont get question 2.... The reason it works with super is that if the list stores any class/type that is a super type of the class specified then an object of the class you specified can be stored in any of those lists because it is extended from that class. ok... I dont understand intrisincally why this is the case. this is just similar to extends keyword. I mean, you can still get the same error where extends keyword can yield. For example, with super, you define a collection: List<? super Integer> l = new ArrayList<Number>(); and you are trying to add an element of type Object... well, you should get a conversion error during runtime I suppose because the instance only allows elements of type Number or its subtypes, not Object, which is none of these...... do you get what I mean? please help thanks
Aeternus Posted July 27, 2006 Posted July 27, 2006 Eh? If you actually put it in a try - catch block you will see that b.meth overrides a.meth in both cases. The only reason it requires that is because the method signature in "a" specifies that the method can throw an exception and so it must be caught. I have no idea why it does it like this but as you can see the method is still overridden. I think it is because at compiler time, it checks these sort of things against the type/class of the variable (ie a not b), for simplicity, as you might do funky things with class loading and so on that might mean that the actual instantiated class might not be known. I know it IS known in this case but they might not bother to actually check this, or conversely there might be some other reason that it does this. Either way, both of those methods are being overriden in the same way.
albertlee Posted July 27, 2006 Author Posted July 27, 2006 again, question 1 class a{ void meth() throws Exception{throw new Exception();} //void meth() {System.out.println("hello from a");} } class b extends a{ void meth() {System.out.println("hello from b");} } public class Test{ public static void main(String[] ag) { [b]a[/b] o = new b(); o.meth(); } } I get a compilation error saying that the method's exception is not caught even though it has been overriden........ hope this shows what I mean clearer... plz help thx
Aeternus Posted July 27, 2006 Posted July 27, 2006 If you read what I said, if you actually put that in a try-catch block and run it, you will see that it has been overriden. As I said I think you get that compiler error because it does checks at compiler time based on the method headers of the methods of the class/type of the variable, not those of the class being instantiated to create the variable.
Aeternus Posted July 27, 2006 Posted July 27, 2006 I also dont get question 2.... ok... I dont understand intrisincally why this is the case. this is just similar to extends keyword. I mean' date=' you can still get the same error where extends keyword can yield. For example, with super, you define a collection: List<? super Integer> l = new ArrayList<Number>(); and you are trying to add an element of type Object... well, you should get a conversion error during runtime I suppose because the instance only allows elements of type Number or its subtypes, not Object, which is none of these...... do you get what I mean? please help thanks[/quote'] Sorry, I don't get what you are saying here - List<? super Integer> l = new ArrayList<Number>(); compiles fine. And if you try to add an Object to it you will get a compile time error like so - Test.java:31: cannot find symbol symbol : method add(java.lang.Object) location: interface java.util.List<capture of ? super java.lang.Integer> l.add(new Object()); The reason for this is that, List<? super Integer> doesn't mean you are creating a List that can hold any type that is a super type of Integer, it means that this variable can hold any list that holds a particular type that is a super type of Integer. There is a difference between the two. Ie, you couldn't for instance do - new ArrayList<? super Integer>(); Because the type that it holds (or the base type at least) must be defined somewhere before it is compiled. Perhaps I'm misunderstanding what you mean?
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now