A place to learn object oriented programming from a Java-centric perspective. We sometimes explore other models as well, when my job demands that I learn something I try to pass it on.
Showing posts with label reddit. Show all posts
Showing posts with label reddit. Show all posts
Friday, June 3, 2016
Tuesday, November 10, 2015
I tried to print my array but all I got was gobbledegook!
This is inspired by a post at /r/javahelp.
A user posted that they were trying to print a sorted array to the console with System.out,println, and that instead of anything that made sense they got [I@4969dd64... So clearly something was wrong and the array was not being sorted.
But that's faulty troubleshooting, as one could tell by just trying to print the array before sorting. You would see basically the same thing.
The reason for this is that when you do a System.out.println on any object, what you're actually doing is calling 'toString()'. If you are printing your own objects for which you've created nice methods that override the default toString, you get nicely formatted output. If you're using an ArrayList you get a nice, comma separated list enclosed in square brackets.
Arrays are more primitive than that. Their default toString is just the one from Object, and it does nothing more than tell you where in the heap the object is located. This is good for verifying that something isn't null, but useless for determining the content.
If you want to see the contents of the array, you've gotta do the work yourself, my friend. And it's not difficult at all. This, for instance, will work for any array of Objects that themselves support a reasonable version of 'toString':
View code
A user posted that they were trying to print a sorted array to the console with System.out,println, and that instead of anything that made sense they got [I@4969dd64... So clearly something was wrong and the array was not being sorted.
But that's faulty troubleshooting, as one could tell by just trying to print the array before sorting. You would see basically the same thing.
The reason for this is that when you do a System.out.println on any object, what you're actually doing is calling 'toString()'. If you are printing your own objects for which you've created nice methods that override the default toString, you get nicely formatted output. If you're using an ArrayList you get a nice, comma separated list enclosed in square brackets.
Arrays are more primitive than that. Their default toString is just the one from Object, and it does nothing more than tell you where in the heap the object is located. This is good for verifying that something isn't null, but useless for determining the content.
If you want to see the contents of the array, you've gotta do the work yourself, my friend. And it's not difficult at all. This, for instance, will work for any array of Objects that themselves support a reasonable version of 'toString':
public static String arrayToString(Object[] theArray) { StringBuilder output = new StringBuilder("["); for (Object o:theArray){ if (output.toString().length() > 1) { output.append(","); } output.append(o); } output.append("]"); return output.toString(); }
View code
Tuesday, February 10, 2015
It's a SERIES OF TUBES!
I've been struck in recent days by a number of posts over at /r/javahelp, all with the same basic theme. They've got some code, and it references a variable name that is declared elsewhere in the program. These nascent developers don't yet understand that just because you mention a variable in one place, that doesn't mean it's available elsewhere.
It is fundamentally important to understand the issue of scope if you ever hope to write programs well. Where you declare things, and what keywords you use when you do so, have a huge effect on the visibility of your variables.
For instance, I often see issues that boil down to an attempt at doing this:
void multiplyTwoNumbers(int numberOne, int numberTwo) {
int numberThree = numberOne * numberTwo;
}
"It doesn't work" they say, or "it doesn't do anything". My friends, it does exactly what you told it to. It's just that you didn't tell it to do anything particularly useful. When this method is finished, it will have multiplied numberOne by numberTwo and assigned its value to numberThree. Then it's done.
Some folks go a bit further:
void callingMethod() {
int numberThree;
multiplyTwoNumbers(5,10);
System.out.println("See? It didn't work! " + numberThree);
}
And they're right, it didn't.
The reason is that the two variables named 'numberThree' are entirely independent of each other. One of them is really 'callingMethod->numberThree' and the other one is 'multiplyTwoNumbers->numberThree' and they have never met.
Naming things the same is meaningless. It might help us to understand our code better, but as far as Java is concerned the variables might as well be called Ben and Jerry. Mmmm, Ben and Jerry.
If you really want callingMethod->numberThree to take on the value you calculated in 'multiplyTwoNumbers' you need to explicitly lay it out. Just as you can't expect your sink to drain properly into the sewer system if you don't actually provide a continuous connection, you can't expect what one method does to just be known by another method.
So make sure you declare multiplyTwoNumbers as an int. Make sure you actually return the value of numberThree at when it's done:
int multiplyTwoNumbers(int numberOne, int numberTwo) {
int numberThree = numberOne * numberTwo;
return numberThree;
}
That will help. You've now installed the pop-up drain thingie in the bottom of the sink bowl. Let's finish the job:
void callingMethod() {
int numberThree;
numberThree = multiplyTwoNumbers(5,10);
System.out.println("It verks! " + numberThree);
}
public void displayWizzLevel() {
System.out.println("Current level: " + wizzLevel);
}
}
Yes, that's perfect valid. In this case, 'wizzLevel' is an instance variable of the class Wizz. Every copy of Wizz you create has its own wizzLevel, and all methods within Wizz can reference or change it.
The key of course, is to look at the brackets. Note that 'int wizzLevel' is inside the brackets for the class as a whole, so it's in scope everywhere within the class.
The key takeaway is that when you want to understand where a variable is available to you, you need to understand that so long as you are still inside the same set of brackets where it was declared you should be good to go.
{
int outside;
{
int inside; //outside is still available
{
int wayInside; //inside and outside are still available
}
//wayInside is gone
}
//inside is gone
}
//outside is gone
It is fundamentally important to understand the issue of scope if you ever hope to write programs well. Where you declare things, and what keywords you use when you do so, have a huge effect on the visibility of your variables.
For instance, I often see issues that boil down to an attempt at doing this:
void multiplyTwoNumbers(int numberOne, int numberTwo) {
int numberThree = numberOne * numberTwo;
}
"It doesn't work" they say, or "it doesn't do anything". My friends, it does exactly what you told it to. It's just that you didn't tell it to do anything particularly useful. When this method is finished, it will have multiplied numberOne by numberTwo and assigned its value to numberThree. Then it's done.
Some folks go a bit further:
void callingMethod() {
int numberThree;
multiplyTwoNumbers(5,10);
System.out.println("See? It didn't work! " + numberThree);
}
And they're right, it didn't.
The reason is that the two variables named 'numberThree' are entirely independent of each other. One of them is really 'callingMethod->numberThree' and the other one is 'multiplyTwoNumbers->numberThree' and they have never met.
Naming things the same is meaningless. It might help us to understand our code better, but as far as Java is concerned the variables might as well be called Ben and Jerry. Mmmm, Ben and Jerry.
If you really want callingMethod->numberThree to take on the value you calculated in 'multiplyTwoNumbers' you need to explicitly lay it out. Just as you can't expect your sink to drain properly into the sewer system if you don't actually provide a continuous connection, you can't expect what one method does to just be known by another method.
So make sure you declare multiplyTwoNumbers as an int. Make sure you actually return the value of numberThree at when it's done:
int multiplyTwoNumbers(int numberOne, int numberTwo) {
int numberThree = numberOne * numberTwo;
return numberThree;
}
That will help. You've now installed the pop-up drain thingie in the bottom of the sink bowl. Let's finish the job:
void callingMethod() {
int numberThree;
numberThree = multiplyTwoNumbers(5,10);
System.out.println("It verks! " + numberThree);
}
This is so important to understand. Local variables go away as soon as the local block (code between { and }) comes to and end. If you don't provide a method for the value to escape the block, it's going to go to the big heap in the sky.
Perhaps part of the problem is that people get confused by the fact that they can do this:
public class Wizz {
int wizzLevel = 0;
public void bumpWizzLevel() {
wizzLevel = wizzLevel + 1;
}public void displayWizzLevel() {
System.out.println("Current level: " + wizzLevel);
}
}
Yes, that's perfect valid. In this case, 'wizzLevel' is an instance variable of the class Wizz. Every copy of Wizz you create has its own wizzLevel, and all methods within Wizz can reference or change it.
The key of course, is to look at the brackets. Note that 'int wizzLevel' is inside the brackets for the class as a whole, so it's in scope everywhere within the class.
The key takeaway is that when you want to understand where a variable is available to you, you need to understand that so long as you are still inside the same set of brackets where it was declared you should be good to go.
{
int outside;
{
int inside; //outside is still available
{
int wayInside; //inside and outside are still available
}
//wayInside is gone
}
//inside is gone
}
//outside is gone
Wednesday, December 3, 2014
Diamonds are not an Object's Best Friend
Once again, I turn to a post I made on reddit for inspiration.
Some people wonder why inheritance branch but never join. Why not tie two different families of objects together and extend both of 'em?
I can see the point of that, one could conceivably build objects that join several disparate branches of inheritance into large and versatile classes. Conceivably. One could also get oneself into a world of trouble, though, and this is most easily explained via the so-called diamond inheritance problem.
Please note that you cannot do this and expect your code to compile, it's just a conceptual demonstration.
Some people wonder why inheritance branch but never join. Why not tie two different families of objects together and extend both of 'em?
I can see the point of that, one could conceivably build objects that join several disparate branches of inheritance into large and versatile classes. Conceivably. One could also get oneself into a world of trouble, though, and this is most easily explained via the so-called diamond inheritance problem.
Please note that you cannot do this and expect your code to compile, it's just a conceptual demonstration.
Diamond.java
public class Diamond extends Class1, Class2 {
}
Class1.java
public class Class1 {
public void doSomething() {
System.out.println("Class1.doSomething()");
}
}
Class2.java
public class Class2 {
public void doSomething() {
System.out.println("Class2.doSomething()");
}
}
Now you have a main that uses the above classes:
public static void main(String[] args) {
Diamond diamond = new Diamond();
diamond.doSomething();
}
So, when you run main, what happens? This is the essence of the diamond problem. You wind up having two lines of inheritance converging on one and run into a quite serious issue.
This basically just scratches the surface of the problem, of course. We can also think about what happens when you try to invoke something in super. The question would have to become 'which super'? There are many potential negative ramifications from the interactions that could be created by allowing this kind of inheritance, and the creators of Java decided that the best way to avoid them was by preventing the issue altogether.
One can achieve most of the positive effects of multiple inheritance via Interfaces and a few helpful design patterns such as Adapter, Composite and Decorator.
Subscribe to:
Posts (Atom)