Static Imports, how they could and should be used
One of the new features of Java 5 is the static import, its quite simple in nature. You have probably done something like the following many times before :
public class Runner
{
public static void main(String[] args)
{
System.out.println("hello");
Math.max(1,2);
}
}
Nothing too complex, you’re refering to static methods on the System and Math classes. If you use a lot of static methods from other classes, you might decide that typing the class name is wasted effort. In this case we can use a static import.
The static import allows you to import the Math and System classes, and then you can call their static methods without having to reference the class name, such as :
import static java.lang.Math.*;
import static java.lang.System.*;
public class Runner
{
public static void main(String[] args)
{
out.println("hello");
max(1,2);
}
}
So as you can see, since we’ve imported the class as a static import, we no longer need to reference the class name before calling a static member.
Of course, there are some drawbacks from taking this approach. Say for example you are performing static imports from multiple classes, that coincidently happen to have methods with the same signature (names and input parameters), how does the class behave in such situation? Consider the following :
package com.test.static1;
public class StaticClassOne
{
public static String hello()
{
return "hello";
}
}
package com.test.static2;
public class StaticClassTwo
{
public static String hello()
{
return "hello";
}
}
package com.test;
import static com.test.static1.StaticClassOne.*;
import static com.test.static2.StaticClassTwo.*;
import static java.lang.System.*;
public class Runner
{
public static void main(String[] args)
{
out.println(hello());
}
}
From this example, we are performing 2 static imports, the static method which we are referencing appears in both imports, so the compiler is unable to know which one it should be using, you would get the following compiler error
reference to hello is ambiguous, both method hello() in com.test.static2.StaticClassTwo and method hello() in com.test.static1.StaticClassOne match
This does leave room for error, and no developer is perfect. This makes me side slightly towards not using static imports unless absolutely necessary, or in situations where I can be sure there won’t be a conflict. Personally, I actually find it easier to prefix static methods with their class names, so I can immediately see which class the static member belongs to.
| Print article | This entry was posted by James Elsey on January 15, 2011 at 12:38 AM, and is filed under Certification, Core Java, Java, SCJP. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |
No trackbacks yet.
Automating android application signing and zipaligning with maven
about 8 months ago - No comments
This is something that has had me tearing my hair out for a few days now, I was pretty much border-line braveheart-ing my screen….
I’ve recently been on a little drive to try to maven-ize my projects. All had been going well until I needed to sign and zipalign my APKs. This post will help you conquer that barrier with the use of some maven plugins.
When using ant, I was able to simply enter keystore details into build.properties and just call “ant release”. Unfortunately that approach doesn’t carry across to maven, and you have to provide some more configuration.
Firstly, before we go any further, I’m going to assume that you already have a maven android project setup, so you have a pom.xml, you’ve configured the maven-android-plugin and you can run “mvn clean install” to build your APK, and “mvn android:deploy” to deploy it to an emulator. If you haven’t got that far, I’d suggest you have a look at one of my previous posts to help get you up to speed.
So, when you want to build your application with maven, you’d run “mvn install”. That will, by default, use a debug key to sign your APK. When we want to build a releasable APK we still want to execute the same install goal, however we’ll want to use a proper key. Luckily, maven provides something called profiles.
In short, maven profiles allow you to still perform the same standard goals, yet they behave slightly different, in the manner of binding extra steps to them, all will come clearer in a moment.
This has already been covered, in a useful post by the guys at Novoda, and various other blog posts scattered around the web. I’ve followed at least 10 tutorials and each time I was unable to get the signing process to work correctly, each time I encountered the following error :
INFO] jarsigner: attempt to rename C:\android-projects\jameselsey_andsam_branch_mavenbranch\target\LanguageSelection-1.0.0-SNAPSHOT.jar to C:\android-projects\jameselsey_ands am_branch_mavenbranch\target\LanguageSelection-1.0.0-SNAPSHOT.jar.orig failed [INFO] ------------------------------------------------------------------------ [ERROR] BUILD ERROR [INFO] ------------------------------------------------------------------------ [INFO] Failed executing 'cmd.exe /X /C "C:\development\tools\Java\jdk1.6.0_20\jre\..\bin\jarsigner.exe -verbose -keystore my-release-key.keystore -storepass '*****' -keypass ' *****' C:\android-projects\jameselsey_andsam_branch_mavenbranch\target\LanguageSelection-1.0.0-SNAPSHOT.jar mykeystore"' - exitcode 1 [INFO] ------------------------------------------------------------------------
I’d tried everything, even copying out the command and pasting into a command window worked, but I could not get it to work from maven. Unfortunately it seems there may be an issue with the maven-jarsigner-plugin (as suggested in the comments on the Novoda post). However fear not, for there is an alternative, the maven-jar-plugin.
The maven-jar-plugin is a similar plugin to the jarsigner plugin, however the signing APIs are now deprecated and it points you to use the (apparently) broken maven-jarsigner-plugin. Using a deprecated API doesn’t particulary concern me in this instance, as its just signing artefacts.
Take a look at my profiles section, copy this into your pom.xml :
<profiles>
<profile><!-- release profile. uses keystore defined in keystore.* properties. signs and zipaligns the app to the target folder-->
<id>release</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<defaultGoal>install</defaultGoal>
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<id>signing</id>
<goals>
<goal>sign</goal>
</goals>
<phase>package</phase>
<inherited>true</inherited>
<configuration>
<keystore>
my-release-key.keystore
</keystore>
<storepass>mypassword</storepass>
<keypass>mypassword</keypass>
<alias>mykeystore</alias>
<verbose>true</verbose>
<verify>true</verify>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<sign>
<debug>false</debug>
</sign>
</configuration>
</plugin>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<configuration>
<zipalign>
<verbose>true</verbose>
<skip>false</skip>
<!-- defaults to true -->
<inputApk>${project.build.directory}/${project.artifactId}-${project.version}.apk</inputApk>
<outputApk>${project.build.directory}/${project.artifactId}-${project.version}-RELEASE.apk
</outputApk>
</zipalign>
</configuration>
<executions>
<execution>
<id>zipalign</id>
<phase>install</phase>
<goals>
<goal>zipalign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
OK, so lets walk through what the above profile does. Firstly, the “id” of the profile is “release”, so when you want to apply this profile, you’d run “mvn install -Prelease”. The “activeByDefault” is set to false, which means you need to use the above arguement, if you flip that over, you won’t need the P flag.
The execution goal is to “sign”, which is the API on the plugin, documented here. We bind this execution on to the standard “package” goal. Then we provide the configuration elements which specify the details of the keystore.
In pure English, this binds the plugin phase onto the package goal of maven, so anytime we run using this profile it’ll execute the jar signing of our artefacts. The “verify” tag gives us extra piece of mind by verifying that the signing was successful afterwards.
I’ve also setup a zipalign profile that will take the APK, zipalign it, and rename it to “*-RELEASE.apk”, so I know that particular APK is the one which I’ll release to market.
So thats it, once more a success, and its just one small step on the ladder to android glory!
Phhew!! Done, finally I was able to shutdown the laptop and go to sleep at 3am….on a school night!!!
Good luck, feel free to comment!
Peace.
String concatenation operator
about 8 months ago - No comments
The string concatenation operator is a bit like a hedgehog, it looks cute and sweet, but try to grab hold of it quickly and you’ll soon know about it…

It’s actually quite simple, however there are some traps that are very easily overlooked (just got caught out with it on a mock test, so I’m here to rant).
The rule of thumb is, that if either one of the operands being “+” is a String, then concatenation will occur, but if they are both numbers, arithmetic addition will occur. Sounds simple right? What do you think the following equate to?
package operators;
public class MockTest
{
public static void main(String[] args)
{
System.out.println(3 + 2);
System.out.println(3 + "s");
System.out.println("s" + 2);
System.out.println("hello " + "world");
}
}
Think you know it? Well its going to be : 5, 3s, s2. The first one is an arithmetic addition, since both sides of the operator are numeric. The second and third are String concatenation, because at least one of them is a String. The fourth one are both Strings, so concatenation once again.
those are the simple ones, however it can get a lot more tricky, have a look at these common ones that those exam creators try to catch you with!
package operators;
public class MockTest
{
public static void main(String[] args)
{
// Inside brackets are evaluated first, so it'll be 7String
System.out.println( (3 + 4) + "String");
/* This is the little bugger that caught me, I vouched for 34String, but its actually 7String.
Concatenation happens from left to right, one at a time, so it'll break it down to:
3 + 4 then...
<thing on left> + "String"
Careful on these ones!
*/
System.out.println(3 + 4 + "String");
}
}
So remember, it’ll evaluate from left to right, one “+” operator at a time. Brackets are always evaluated first however.
Take my examples and have a play!
Happy coding (or raging at the screen if the mock tests catch you out…)
The instanceof Operator
about 8 months ago - No comments
The instanceof operator is a great way for checking if a reference variable is of a given type, in other words, does it pass the IS-A inheritance test. Ask yourself, is a Cat a Dog? A Dog a Cat? Or is a Cat an Animal and a Dog an Animal? In true Harry Hill style, there is only one way to decide….FIGHT!!!!

Have a look at my following example :
package operators;
public class InstanceOfOperatorDemo
{
public static String myString = "James";
public static void main(String[] args)
{
// myString variable is of type String, so returns true
System.out.println("myString instanceof String : " + (myString instanceof String));
// Even though myString is a String, a String is actually an Object (extends from Object), so the below is also true
System.out.println("myString instanceof Object : " + (myString instanceof Object));
B b = new B();
// B is technically an instance of the implemented interface, so this should return true
System.out.println("B instance of Foo : " + (b instanceof Foo));
// This should return true also for the same reasons as above
System.out.println("A new A() instance of Foo : " + (new A() instanceof Foo));
// An A isn't an instance of a B, its the other way around; a B IS-A A, so returns false
System.out.println("A new A() instance of B : " + (new A() instanceof B));
// Any checks with null always return false, since null isn't an instance of anything
System.out.println("null instanceof Object : " + (null instanceof Object));
int[] myInts = new int[10];
// Even though this array contains primitives, the array itself is still an Object at heart..
System.out.println("myInts instanceof Object : " + (myInts instanceof Object));
}
}
interface Foo {}
class A implements Foo {}
class B extends A {}
As we can see, a String IS-A Object, so that will pass the instanceof test. It is also important to note, that interfaces are also included in the instanceof check, so by checking if a class or a subclass is an instanceof an interface will return true.
Arrays, no matter what they hold, are always objects themselves, so they can be checked for instanceof too, of which they are Objects, as shown above.
It is important to note, that you can’t use instanceof across class hierarchies, this is something they’ll try to trick you out of in the exam, such as comparing a cat against a dog, you can compare up the hierarchy, but not across it, have a look at this example :
package operators;
public class InstanceOfOperatorCrossHierarchyDemo
{
public static void main(String[] args)
{
// a Cat IS-A Dog, plain and simple
System.out.println("Is cat an instanceof Animal? : " + (new Cat() instanceof Animal));
// This will fail to compile, since we are not allowed to instanceof check across the
// object hierarchy, a Cat is not an instance of Dog, so compiler fails
System.out.println("Is cat an instanceof Dog? : " + (new Cat() instanceof Dog));
}
}
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
That’s it! A nice and easy one, please leave me comments if you can suggest improvements!
Happy coding.
Compound Assignment Operators
about 8 months ago - No comments
There are various compound assignment operators, however it is only necessary to know the 4 basic compound assignment operators for the exam, being as follows:
- += (Addition compound)
- -= (Subtraction compound)
- *= (Multiplication compound)
- /= (Division compound)
Essentially, they’re just a lazy way for developers to cut down on a few key strokes when typing their assignments. Consider the following examples that I’ve created to demonstrate this:
package operators;
public class AssignmentsOperator
{
public static void main(String[] args)
{
int x = 5;
// This is the "longhand" way of adding 5 to x
x = x + 5;
// You could use the compound assingment as follows, to achieve the same outcome
x += 5;
// Lets look at the other 3...
// This...
x = x - 1;
// ...is the same as
x -= 1;
// This...
x = x * 10;
// ...is the same as
x *= 10;
// This...
x = x / 10;
// ...is the same as
x /= 10;
}
}
That is it! Any suggestions, please let me know!
Happy coding.
Static members…
about 8 months ago - 1 comment
Statics are a rather strange beast, they belong to no instance of a class, they have no fixed abode, other than their class..
Consider the following example :
package declarations;
public class StaticMembers
{
private static String message = "hello";
private String instanceMessage = "hello from the instance!";
public static void main(String[] args)
{
// You "can" create a new instances and call the static member, but its a bit redundant....
StaticMembers staticMembers = new StaticMembers();
System.out.println(staticMembers.message);
// ..Since you can just call it directly, as statics don't belong to any instances
System.out.println(StaticMembers.message);
// You can't do this, since how would the static know which "copy" of the instance method to get?
System.out.println(instanceMessage);
// ..But you can do this, since you have an instance..
System.out.println(staticMembers.instanceMessage);
}
}
Statics don’t belong to any instance, they belong to the class itself. So there is only ever one copy of a static member (method or variable). Statics can’t ever access an instance variable directly, why not? Think about it, if a static wants to obtain an instance member variable, there may be hundreds, how would it know which one to get? You can however, declare and initialise an instance member from a static, and then access its members, as we’ve done in the example above.
Take my example, and have a play with it, statics are actually quite straightforward once you understand how they behave, but be careful, the exam will try and trick you out with statics accessing instance members directly!
Happy coding!
Local Variables, as far as the SCJP is concerned
about 8 months ago - No comments
Local variables are variables that are declared locally, funny that eh? This one should be nice and easy, lets have a look at a quick example :
package scope.localvariables;
public class LocalVariables
{
private String instanceString;
public void doSomething()
{
// local variable called 'localS', initialised
String localS = "hello";
// Another initialised local variable, with the final modifier so it can't be changed once initialised
final String localS2 = "hello again";
localS = "I've changed, since I'm not final";
System.out.println(localS);
System.out.println(localS2);
}
public void doSomethingElse()
{
String localS;
// Compiler won't like this, since you haven't initialised 's' with anything
System.out.println(localS);
}
}
Some really easy things to remember, local variables don’t ever get initialised automatically, so you have to do that yourself. The method above, doSomethingElse, the variable gets declared, but never assigned a value. The compiler won’t assign a default value like it does for an instance varible, so the compiler will complain on the line which tries to print the value to system out. So whatever you do, if you declare a local variable but don’t assign it a value, make sure that you DO assign it a value before you try to use it.
Since variables are declared within a method, they can only be accessed, and actually only live for the duration that method is executing. So if you declare a variable inside a method, but try to access it elsewhere, your code will fail to compile since its scope is only available to the method the declaration resides in. Careful of this one, there are a lot of questions in the exam which will try to catch you out with this!
Local variables can take the final modifier if required, so that once assigned a value it can’t be changed.
Also note, that local variables live on the stack, and not the heap.
Lastly, it is possible to have local variables with the same names as instance variables, this is known as shadowing. You’ve probably seen this countless times before without actually realising, since its quite popular with constructors (with may also have local variables)
package scope.localvariables;
public class MyClass
{
private String name;
private int age;
// Overridden constructor, with 2 local variables that shadow instance variables
public MyClass(String name, int age)
{
this.name = name;
this.age = age;
}
public static void main(String[] args)
{
MyClass myClass = new MyClass("James", 25);
System.out.println("Name is : " + myClass.name + " Age is : " + myClass.age);
}
}
Thats it, hope you’ve enjoyed the show folks, please leave comments or improvement suggestions!
Thanks!
Other modifiers, for members…
about 8 months ago - No comments
We’ve already touched upon various modifiers, for classes (both access and non-access), but there are also some more modifiers for members, as detailed here.
We have the following modifiers for members :
- final - Can’t be overridden
- abstract - No implementation specified, subclass must implement
- synchronized - Only a single thread of execution can pass through at a given time
- native - Implemented by a 3rd party piece of code (C++ for example)
- strictfp - Enforces foating point precision and doesn’t let the JVM do its own way
To make things easier, you don’t actually need to know how the strictfp or native work for the exam, but you’ll need to be aware of them. Strictfp guarantees precision on floating point calculations (otherwise the JVM implements precision however it feels), and the native is used for external implementations, such as implementing methods in C++.
All you must remember with these, is that the native modifier applies only to methods, but the strictfp modifier applies to both classes and methods. So lets have a look at a brief example then pass aside on these two and focus on the ones that matter.
package modifiers;
// The class can be marked as strictfp too!
public strictfp class NativeAndStrictfp
{
// A normal method
public String giveMeAString()
{
return "hello";
}
// strictfp method
public float giveMeAFloat()
{
return 123.123f;
}
// pretend its abstract, we don't provide implementation so use a semi colon not braces!
public native String doSomeNativeCodingAndReturnAString();
}
As with classes, the final modifier does the same thing, it states that the particular method can’t ever be overridden in a subclass, simple as that.
The same applies to the abstract modifier, abstract methods are declared with a signature, a return type, an optional throws clause, but are not actually implemented. The first concrete subclass to extend from the class has to override that method and provide its implementation.
Consider the following example :
package modifiers;
// Don't forget, that since we have one abstract method, the whole class must be marked abstract!
public abstract class OtherMemberModifiers
{
// I'll get inherited, since I'm public, but I can't be overridden.
public final String giveMeAString()
{
return "hello";
}
// Remember, semi colon, no braces!!
public abstract String giveMeAnotherString();
}
class ClassThatExtends extends OtherMemberModifiers
{
// I'm the overridden abstract method above!
@Override
public String giveMeAnotherString()
{
return "another string";
}
}
Remember, abstract methods can’t be private or final, otherwise how would it ever be overridden? Think about it!
Lets cover the synchronized modifiers. Synchronized means that only one thread of execution can pass through that method at a given time, and you can use the modifier just like any other, they can also be marked as final, to prevent subclasses from overriding your implementation. Lets have a look :
package modifiers;
public class SyncModifiers
{
// Just a synchronized method ;)
public synchronized String giveMeAString()
{
return "hello";
}
// I can't be overridden by a subclass
public final synchronized String giveMeAnotherString()
{
return "hello again!";
}
}
The synchronized modifier can also be used on code blocks, but we’ll cover that more in the threading posts.
Well thats it! I hope I’ve explained things well, if you find any mistakes or want to suggest improvements, please let me know!
Happy coding!
Class Modifiers (non-access)
about 8 months ago - 1 comment
In addition to class access modifiers, classes can also be marked with non-access modifiers. These modifiers imply rules on a class, but are not necessarily linked to access rights.
The following non-access modifiers are available:
- final - The class can’t be extended.
- abstract - The class has to be extended, and can’t be instantiated on its own.
- strictfp - Ensures that you always get exactly the same results from your floating point calculations on every platform your application runs on. If you don’t use strictfp, the JVM implementation is free to use extra precision where available.
Lets make things easier, its worth knowing what strictfp is, but its not on the SCJP exam so you don’t need to know how to use it, so you can almost brush that aside after remembering the information above.
Final classes can’t be extended / subclassed. Thats great, for those times when you don’t want people extending your classes, mark them private. Take the String class for example, that is private, as they didn’t want developers extending it and altering the way Strings behave, that would cause nightmares for all sorts of applications!
The other non-access modifier is the abstract modifier. There may be times when you don’t want people to instantiate your class. Say that you had a class Car, but you didn’t want people to start creating Car objects all over the place, you want them to extend your Car class and create their own functionality, you might choose to make Car abstract, like the following example:
package declarations;
public abstract class Car
{
public void doSomething()
{
}
}
class SportsCar extends Car
{
public static void main(String[] args)
{
// do SportsCar stuff here
}
}
Don’t forget, that a class can’t be both final and abstract, as the contradict each other, one can’t be subclassed, yet the other must be subclassed, it just won’t work!
We’ll touch more upon abstract classes in the interfaces sections, as interfaces are essentially 100% abstract classes, but more about that later :)
Class Access Modifiers
about 8 months ago - 2 comments
Class access modifiers define who can see the class, you use it on a daily basis, have a look at the following :
public class ClassAccessModifierExample
{
public static void main(String[] args)
{
// do something!
}
}
There you go, you said “public class”, thats you saying that this class is public, and anything can access it.
It is important to note, that even though a class may be declared as public, you can still have private members within that class. The class access modifier merely states who can gain access to the class.
There are 3 class access modifiers, in other words there are 3 keywords that you can use :
- public - any class can import and use your class.
- protected - any class in the same package as yours can import and use your class, additionally, any class that resides in another package, can extend and use your class. This is only really useful for inner classes.
- private - no other top level class can import and use your class. This is a bit of an odd situation but is useful for inner classes, detailed in another post.
Those are your 3 keywords, however there is actually a 4 level of access, called default. The default access level doesn’t use a keywork, so it would be used like follows :
class ClassAccessModifierExample
{
public static void main(String[] args)
{
// do something!
}
}
So our 4th access level is :
- (default) – also called “package-private”, this class is only visible to classes in the same package.
So what is all the fuss about visibility about? Well it lets classes do various things,such as :
- Create an instance of the class
- Extend, sublass that class
- Access members of that class
One last thing to remember, is that a source file can only ever have one public or default class, it can’t have more than one of those. Also, private and protected can’t be at the top level.
- You can’t have a private top level class, its useless.
- You can’t have a protected top level class, since it goes against the principle of the protected access.
Consider the following valid example
// I'm public, and my name matches that of the source file
public class ClassAccessModifierExample
{
public static void main(String[] args)
{
// do something!
}
// You can have as many protected classes as you want, accessible
// to any classes that extend from the top level class, ClassAccessModifierExample
protected class MyProtectedClass
{
}
// Have as many private classes as you want, they are only accessible to the top level class,
// ClassAccessModifierExample
private class InnerClassThatIsPrivate
{
}
// Nothing wrong with having a public inner either!
public class PublicInner
{
}
}
// Default class here
class TopLevelDefaultClass
{
}
Thats it! If you’ve got any suggestions on how to improve this, please leave a comment :)
Varargs, for those times when you’re not sure how many parameters you’ll have…
about 8 months ago - No comments
As of Java 5, methods are now able to accept from 0 to many arguments. Sounds confusing, but you could actually be using it already without knowing, how about looking at your main methods?
public static void main(String... args)
As we can see above, var args are declared as TYPE… NAMEOFVARIABLE. Lets take a look at a basic example I’ve created :
public class MethodsWithVarArgs
{
public static void main(String... args)
{
// print out various numbers, just 'cos we can
printNumbers();
printNumbers(1);
printNumbers(1, 2, 3, 4);
// Print out information on various dice.
printDieTypeAndItsNumbers("Die with no numbers on it");
printDieTypeAndItsNumbers("Dodgy die that could never exist...", 1);
printDieTypeAndItsNumbers("D6", 1, 2, 3, 4, 5, 6);
printDieTypeAndItsNumbers("D9", 1, 2, 3, 4, 5, 6, 7, 8, 9);
}
public static void printNumbers(int... numbers)
{
for (int i : numbers)
{
System.out.println(i);
}
if (numbers.length == 0)
{
System.out.println("I didn't get passed in anything... :(");
}
}
public static void printDieTypeAndItsNumbers(String dieType, int... numbersOnTheDie)
{
System.out.println("Die : " + dieType + " has the following numbers on it...");
for (int numberOnDie : numbersOnTheDie)
{
System.out.println(numberOnDie);
}
}
}
As we can see in both methods, both accept a vararg input parameter. We can invoke these methods with no ints, or many ints, the methods are totally accepting of it!
In effect, the varargs are being treated as arrays, but we don’t have the hassle of getting our hands dirty creating the arrays, which is always a bonus.
One important thing to notice, is that you can only ever, EVER have one vararg parameter, and it must be the last one. The following are all invalid:
public static void invalidBecauseMoreThanOneVarArg(String... first, String... second) {}
public static void invalidBecauseVarArgNotAtEnd(String... varargs, String message) {}
For the above, they are invalid, why? Think of it this way, if you had 2 vararg input parameters, how would you know where one ends, and the other begins?
So, feel free to use varargs, but make sure you only have one, and its at the end of all your input parameters.





about 3 months ago
That’s interesting but couldn’t you also just import the class without the keywork static? I think I’ve seen static imports that way.
Thanks \nick