3/14/2012

Why IDEA is better than Eclipse

Holy war

There are "eternal" questions that have no single correct answer. E.g. what is better: Windows or Linux, Java or C#; who is stronger, Chuck Norris or Van Damme.
One of such holywars is selection of the best Java IDE:


There is a lot of disputes on this topic discussing which of them has more plugins, shortkeys and so on. There is so many differences that it's difficult to decide what of them are the most important. As a result, people typically claim that both IDEs are equal in their capabilities, and choosing of one of them is a matter of taste.

I claim that it is not just a matter of taste. There are objective reasons why
Intellij IDEA (as a Java IDE) is definitely better than Eclipse.

I am not going to compare lot of tiny differences like plugins, shortcuts etc - there is a plenty of blog posts on this topic, like this one: [part 1] [part 2] [part 3]. I am going to show just one major difference between IDEA and Eclipse. Typically, neither IDEA nor Eclipse fans know it. The first ones used to it and cannot imagine that IDE can miss it. The second ones used to live without it and cannot imagine that IDE can do such things. Moreover, Eclipse users typically don't notice this difference when trying IDEA for fun, just because they used to work "in Eclipse mode".

Why am I so confident?

Before I start, let me explain who I am and why is my opinion worth listening to.

I've been working on Eclipse for 5 years, I knew it very well, I wrote plugins for it and sincerely loved Eclipse. Then I switched to another company, and was forced to move to IDEA. After surviving a serious break-up, I finally realized why is IDEA steeper. And now I have been working on IDEA for a couple of years. So I know both IDEs well and can compare them.

The main difference between IDEA and Eclipse

The main reason why IDEA is steeper is: IDEA feels context. That's what JetBrains employees mean when they name IDEA intelligent. What it really means? IDEA indexes the whole your project, analyses everything it has, and even builds the syntax tree. Thanks to it, at any time, wherever you put the cursor, IDEA knows where you are and what can be done there.

Still not clear? Do not worry, it will become clear in the following examples.


This ability to understand the context is expressed in many, many ways, here are just a few.

1. Debugging

As a part of debugging process, we often want to evaluate some expression to see its value. In Eclipse you need to select this expression. It's important to exactly select the whole expression, otherwise Eclipse cannot evaluate it. Now you press Ctrl+Shift+I and see the value of expression.



With IDEA you don't need to select anything. You just put cursor at any place inside your expression (at method hasAttribute in given case) and press Alt+F8. IDEA understands which expression you probably need and shows a dialog window suggesting several possible variants for your expression. You can also edit and immediatelly evaluate the expression in this dialog. Very convenient! After trying this feature, you just cannot debug in Eclipse anymore.

It turns out that both IDEs, in principle, allow you to do the same thing. But with IDEA it's much easier and faster. I'm serious, the difference is huge - it's just Heaven and Earth. In this small dialog window IDEA will provide autofilling, syntax highlighting and everything you need.

2. Autocomplete

Autocomplete is what distinguishes any IDE from the notepad. In this area feeling the context gives IDEA a qualitative advantage. For example, we started writing a line of code:
assertElement(By.id("errorMessage"), vi
and now we want to find what options do we have: what can start with letters "vi".

What IDEA does? Not waiting for any keystrokes, it immediatelly understands that method assertElement wants an Condition class instance as a second parameter, and there is a static variable in class Condition with name visible. And IDEA immediatelly suggests the only valid option:


And what Eclipse does? Alas, it does not understand the context. It does not know that the cursor is located where the second parameter of the method assertElement should place. So when you press the sacred Ctrl + Space, Eclipse simply shows everything in Universe that begins with the letters "vi":


In a beautiful pop-up window we see a lot of pretty-highlighted well-documented useless information. :(

3. Refactoring


Professional programmers are able to be productive using refactorings provided by their IDE. All modern IDE offer a very impressive set of refactorings. But then again, IDEA refactorings are intelligent. They realize what you want, and offer different options that are suitable for most situations.

For example, suppose we have a method assertErrorMessageIsHidden:
public void assertErrorMessageIsHidden() {
    assertElement(By.id("errorMessage"), Condition.visible);
  }
And we want the String "errorMessage" to come as a parameter to this method.

Let's start from IDEA. Put the cursor to any place inside the string "errorMessage", press Ctrl+Alt+P (meaning "parameter"), and IDEA suggests what expression we probably could extract to a parameter:


As soon as the expression "errorMessage" is selected, IDEA suggests several possible names for this parameter.

Wow every month

You will be surprised many, many times at how smart behaves IDEA, offering options for the name of something. It takes into account method name, variable type and even value, and names of such variables in other places, and those names that you gave such a variable before - except that your zodiac sign is ignored. Believe me, you will say: "Wow, IDEA is able to do such thing too?" at least once every month.

And now let's look that Eclipse suggests.

Do not forget: select the entire expression "errorMessage" (always with quotes, or get a wild message "An expression must be select to activate this refactoring"), choose a refactoring "Introduce parameter" (from the menu, there is no hot key) and get about the same result. However, Eclipse doesn't suggest options for a parameter name, but thanks for that.


Conclusion

If we are choosing a Java IDE then Intellij IDEA is definitely better than Eclipse. It's not just a matter of taste. IDEA is objectively better. It lets you to quickly and easily write and change the code, suggests appropriate names, finds the appropriate methods. It does not require you to exactly select the expressions, but guesses what you want to do and how you wanted to name it. IDEA anticipates and suggests.

P.S. Remarks

I claim that IDEA is better Eclipse just as a Java IDE. If you are considering them in some other capacity - such as the IDE for other languages ​​(C++, Python, Scala), or as a platform for building desktop application, Eclipse may very well win.

Actually it follows from their definition. Eclipse has positioned itself as an abstract platform for building anything with plug-ins, and IDEA is positioning itself as a "intelligent IDE for Java". So it is.

For the sake of interest I will try to name some aspects in which the Eclipse is probably better than IDEA:
  • Eclipse looks better (thought, this is a matter of taste). All this fuss about SWT and native controls was worth it. Eclipse looks like a solid well thought-out application with pretty fonts and icons, while the IDEA at first glance looks like a stunt toy with a lame hack icons and awkward Swing interface.
  • Eclipse has a richer project structure support. In IDEA you have a project consisting of modules. In Eclipse you have a workspace consisting of projects which can be closed/opened, grouped and hidden. But do you actually need it?
  • It seems to be easier to write plugins for Eclipse.
  • IDEA uses more resources (memory) than Eclipse. This actually makes sense - because he knows so much.
  • And finally, Eclipse is free. However, IDEA Community Edition is just enough for me.

My final recommendation

If you need pretty icons, platform for creating desktop applications, IDE for C++, or you work on an old weak laptop, then Eclipse is problably better choice for you. But if you are a serious Java developer, and you need a fast and convenient tool that helps you focus on the problem instead of distracting, then the IDEA this is exactly what you need.

I hope one more holywar is over today.

Develop with pleasure!

1/28/2012

KISS

Recently I did talk about KISS principle in software development:
Keep it stupid simple

After a short introduction (see slides below) participants were given a number of problems for solving. The problems were very simple, so the goal was not just to solve them, but to find as simple solution as possible.

Participants could post their solutions to an online server. Here it is:

http://tasks.jelastic.com/

Now posting new solutions is disabled; you can only browse all provided solutions.
In future versions it's planned to implement automated testing of provided solutions.

3/03/2011

WTF is business logic?

Question


I have heard many times the term "logic" or "business-logic" in discussions about software development. For instance:

  • (about unit-tests) it's not necessary to achieve 100% code coverage, testing of logic is god enough.
  • (about web applications architecture) controller should not contain any business-logic, it only should call other classes's methods.
  • VIEW layer (e.g. JSP files) should not contain any business logic

Now tell me, what is "logic"? Does it mean any IF in code? But, I don't think that code without IFs ever existed. Or "business-logic" means any information that came from client? Actually I believe that all what we do is initiated by client, so all our code does contain information from client. That's why I never could understand what the fuck is "business logic".


Answer


Probably this question could be easier answered if you use term "knowledge" instead of "logic". "Logic" is any knowledge about the code, or about how your code should behave. It may be also expressed like "logic is anything that you ever wanted to comment".


Example


Let's consider class Account for example.

Once upon a time Internet connection disappeared from our office for two hours. I actually could not work because SVN, jira, dabase etc. were not available. And I decided to exercise in writing unit-tests. So I found class Account:

public class Account
{
  // Negative amount means that account is prepaid
  private BigDecimal amount;
  public BigDecimal getAmount() {
    return amount;
  }

  // And 20 getters/setters more
}

At the first glance, is there anything to test? Is there any IFs? Is there any logic? It seems that no. No logic, no IFs - nothing to test. I wanted to skip this class and find something more complicated, but eventually got sight of the comment near the amount field:

Negative amount means that account is prepaid


Wait, what is that?
It turns out that the amount is a total debt of the client, and if the client has made an advanced payment, then this "debt" is negative. Well, this is some knowledge about the code. Let's try to convert this comment to unit-test:

public class AccountTest
{
  @Test
  public void negativeAmountMeansThatAccountIsPrepaid() {
    Account prepaidAccount = new Account(-123);
    assertTrue(prepaidAccount.isPrepaid());
  }
}

Since we check for negative amount, it also would be reasonable to check for positive amount:
  @Test
  public void positiveAmountMeansThatAccountIsInDebt() {
    Account indebtAccount = new Account(456);
    assertFalse(indebtAccount.isPrepaid());
    assertTrue(indebtAccount.isInDebt());
  }

We have eventually got two new methods isPrepaid and isInDebt, which implementation is of course straightforward for you:
public class Account
{
  private BigDecimal amount;
  public boolean isInDebt() {
    return amount > 0;
  }
  public boolean isPrepaid() {
    return amount < 0;
  }
}

You see, we have got IF's in our code!
What's next? You should have thought: probably, these IF's already were somewhere in the code?

I had to search for such IF's for a while, but it was worth it. These IFs were in JSP files, which is the "view" layer, which should not contain any business logic by definition:

<% Positive amount means that account is in debt %>
<c:if test="${account.amount > 0}">
  <span style="color: red;">Please pay your debt!</span>
</c:if>
<c:if test="${account.amount < 0}">
  <span style="color: green;">Advanced payment</span>
</c:if>

Wow, we have found the second test-case. Cool. Now, since we have methods isInDebt and isPrepaid, we can abandon logic in JSP:

<c:if test="${account.inDebt}">
 <span style="color: red;">Please pay tour debt!</span>
</c:if>
<c:if test="${account.prepaid}">
  <span style="color: green;">Advanced payment</span>
</c:if>

Is it better? I believe it is. This is encapsulation - hiding the implementation of logic from outer code. Class Amount is the only in the whole world who knows how to define, if client is in debt or has advanced payment. Other code doesn't ever know how it happens - it can get resulting boolean throught methods isInDebt and isPrepaid. Theoretically "in debt" and "advanced payment" information could be stored in different columns, or event different tables, or even different databases. Nobody knows. Thanks to two methods, this logic is encapsulated in class Account.

If you still don't believe, compare code size:


Before
<%-- Positive amount means that account is in debt %-->
<c:if test="${account.amount > 0}">
  <span style="color: red;">Please pay your debt!</span>
</c:if>


After
<c:if test="${account.inDebt}">
  <span style="color: red;">Please pay your debt!</span>
</c:if>

You can go further and create two different methods getPrepaidAmount() и getDebt() instead of method getAmount(). Additionally they can check, if the client has really debt or advanced payment. As a result, it's impossible now to write a code like this one:

<%-- No matter if you have advanced payment, 
I still think that you are in debt! %>
<c:if test="${account.amount != 0}">
  <font color="red">Please pay your debt!</font>
</c:if>

Ideally, method getAmount() should be removed. This would be the real encapsulation: hiding of data and code and making them available only throught API. I think I would remove all of 20 other getters/setters if internet connection wouldn't get back to our office that day.

Moral

As You see, unit-tests lead you to readable, object-oriented code. Are you still thinking that your code doesn't contain logic? Are you still thinking that you code has nothing to test?

Of course it has!

5/13/2010

Why the devil invented javadoc?

It is believed that a good program should be well documented.

SUN company even creared a special format javadoc - "a standard for documenting classes Java". In fact, it was quite a common case in my experience, when a code did not pass Code Review just because some of its methods lacked comments.

Today I'll tell you why the comments are evil.

Start from example


Consider the real example from live code. This is a real code written quite diligent programmer who was not lazy and wrote a commentary on his method. Pleased with himself, he went to pour himself a cup of coffee from the machine. While he is going to the office kitchen, let's take a look at what we have here.

public class AddressUtil {
/**
* Format string as address, expected input
* format:"EE ; ;Tallinn;Narva mnt;120B;831;10127"

*
* @param flatAddress
* @return Formatted address

*/
public static String toString(String flatAddress) {......}
}


Excellent! We have a correctly designed a format javadoc, from which a special program can generate HTML-documentation. As it is easy to see that (theoretically) makes this method.

Where is the hidden devil?


But where are those little things that hid the devil? And here they are:

  1. Very soon this documentation becomes outdated, because some other developer will come and change the code, but forget to change the documentation. It may even be the same developer, because while he was standing in line for coffee, it occurred to him that he forgot to process one rare case. When he comes back, he adds the desired IF into the code, but forgets that he already has javadoc, which must be maintained.

  2. This documentation does not describe the mass of cases: how the method behaves, if the input comes null or empty string? What if address contains house number, but misses apartment number (ie bourgeois took home a whole)? What's that empty parameter between the "EE" and "Tallinn"?

  3. Documentation doesn't describe what this method returns.

  4. There are three extra lines in the documentation: "*", "@ param flatAddress" and "@ return Formatted address". Just think: they occupy a large part of the documentation, and they are absolutely useless!

It can be summarized in two words: "Comments lies"! That's all. You cannot do anything with this, except the cases when you have a special position for people how review all the documentation periodically. Damn, do you really want to do that?

The Magic


Now let's do a focus-pocus and create The Magic. We make a few magical passes. Sim salyabim, Ahalan-mahalay, Lyaska-masyaski ....


  1. Pass # 1: Everything that is written in red, we turn to the name of the method: toString -> formatAddress.

  2. Pass # 2: Everything that is written in blue, transfer to unit-test.

  3. Pass # 3: (my favorite) The text, written in green, wipe fuckin. Do not spare him, he was born in vain!

What we did in the end?

public class AddressUtil {
public static String formatAddress(String flatAddress) {......}

@Test public void testFormatAddress() {
assertEquals("Narva mnt 120B-831 10127 Tallinn",
AddressUtil.formatAddress(
"EE ; ;Tallinn;Narva mnt;120B;831;10127"));
}

}


What the new version better than the old?

  1. It's just shorter: there are now 4 rows compared to former 8.

  2. This test will never becomes obsolete, because it will run automatically every time you build the project, and if the programmer change the code, and forget about the method, it immediately pops up.

  3. You can describe all the rare cases: the empty string, missing keys, invalid values, etc.



In short,
GOOD TITLE + TESTS = DOCUMENTATION


rather, executable documentation, or documentation that can not only read but also "run", automatically checking that it is still adequate.

It is said that Confucius was a poster over the bed:

Convert comments to executable documentation



Afterword



I'm just afraid that our brave programmer, returning from the kitchen, will not understand the focus, because he had not seen our magical movements. He will get mad only because SOMEONE Nagle has deleted his comments, and he will try to find us and kill for such subversive activities.

... And his coffee gets cool in the meantime. Well, no so bad: after all, coffee, they say, is harmful. So, we did today did one good thing.



Andrei Solntsev

http://funny-java.blogspot.com/

PS. Well, I must admit the documentation is still needed in some cases, for example, when you writing public API, which other people will download and use. This API is hardly going to be changed, so it's possible to maintain the documentation. But you must remember that documentation is not just comments in code - this is a part of you product, which requires resources for creation and maintenance. Tule of thumb: prefer NOT to writing comments. Write them only if it's really unavoidable.

12/23/2009

HireBug

On the last DevClub meeting I presented a HireBug - a "server-side Firebug".

This is presentation in russian:
http://www.slideshare.net/asolntsev/hire-bug

Update: presentation video (also in russian):
http://www.devclub.eu/2009/12/29/andrei-slontsev-hirebug/

11/27/2009

Vaadin demo with Maven

I like Vaadin - Java web framework created by Finnish guys from ITMill.

Vaadin homepage: http://vaadin.com

This is demo web application built with Vaadin+Maven which performs search files on server.
It was really easy to create it!



All you need is to include Vaadin depenency to your pom.xml and create class that extends com.vaadin.Application. No any XMLs, jsp etc.


Full source code:
http://dl.dropbox.com/u/2856647/vaadin-demo-webapp.zip

Screencast:
http://dl.dropbox.com/u/2856647/VaadinDemo.swf

Short overview of Vaadin: http://www.streamhead.com/vaadin-promote-great-gwt-toolkit/

11/26/2009

Jetty Runner

What is JettyRunner?
Jetty Runner is an Eclipse plugin that allows developer to easy run web applications from your projects.

How it works
- Scan all projects in workspace
- Detect web applications projects
- Let user choose web applications to run
- Let user choose the database to connect to
- Run Jetty

Presentation
http://www.slideshare.net/asolntsev/eclipse-jetty-runner

Screencast
http://dl.dropbox.com/u/2856647/JettyRunnerDemo.swf

Installation
Write me in comments if you are interested. Then I will publish the sources/binaries.

PS. This plugin was presented at Eclipse DemoCamp in Tallinn 27.11.2009

5/20/2009

ThreadSafeDateFormat

Problem
As known, Java class SimpleDateFormat is not Thread-safe.
It means that you cannot declare a static member DateFormat in class:

private static final DateFormat DATE_FORMAT = new SimpleDateFormat( DATE_PATTERN ); // WRONG!

Using this member by 2 concurrent treads will lead to error.

What is a solution?
One solution is to create new SimpleDateFormat( DATE_PATTERN ) each time you need to parse a date. Another solution is to create a Thread-safe version of DateFormat.

That's it:


import java.lang.ref.SoftReference;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
* Thread-safe version of java.text.DateFormat.
* You can declare it as a static final variable:
*
* private static final ThreadSafeDateFormat
* DATE_FORMAT = new ThreadSafeDateFormat( DATE_PATTERN );
*/
public class ThreadSafeDateFormat extends DateFormat
{
private static final long serialVersionUID = 3786090697869963812L;

private final String m_sDateFormat;

public ThreadSafeDateFormat(String sDateFormat)
{
m_sDateFormat = sDateFormat;
}

private final ThreadLocal m_formatCache = new ThreadLocal()
{
public Object get()
{
SoftReference softRef = (SoftReference) super.get();
if (softRef == null || softRef.get() == null)
{
softRef = new SoftReference(
new SimpleDateFormat(m_sDateFormat) );

super.set(softRef);
}
return softRef;
}
};

private DateFormat getDateFormat()
{
return (DateFormat) (
(SoftReference)m_formatCache.get()).get();
}

public StringBuffer format(Date date,
StringBuffer toAppendTo, FieldPosition fieldPosition)
{
return getDateFormat().format(
date, toAppendTo, fieldPosition);
}

public Date parse(String source, ParsePosition pos)
{
return getDateFormat().parse(source, pos);
}
}



The main idea of this class is storing separate instances of SimpleDateFormat for separate Threads in ThreadLocal variable. If 2 concurrent threads try to parse date, the will use 2 different instances of SimpleDateFormat.

3/14/2009

Functional Programming in Java

This is my seminar about Functional Programming and using its ideas in Java.

It was done in year 2006.

http://www.slideshare.net/asolntsev/functional-programming-in-java

8/10/2008

proxy-scm sources

As some people ask, I publish my sources of maven-scm which include proxy-scm feature.

You can either build it from sources, or upload binaries to your company/local repository.

Sources (downloaded from apache source repository and modified):
http://andrei.solntsev.googlepages.com/maven-scm-1.1.Internal-sources.zip

Binaries (built with JDK 1.5, compatible with JDK 1.4):
http://andrei.solntsev.googlepages.com/maven-scm-1.1.Internal-binaries.zip

If you build from sources, you need to change section in pom.xml files.

To start using this modified version of maven-scm plugin, you need to write it into your pom.xml's "pluginManagement" section:

<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
<version>1.1.INTERNAL-SNAPSHOT</version>
</plugin>
</plugins>
</pluginManagement>

3/05/2008

How to find which jar file contains your class at 'runtime'

It's often happening when you want to get know where JVM takes some class from.
If this class potentially can be located in several folders/jars, you never know which one of them JVM uses.

There is a simple method allowing this:

/**
* Method returns code source of given class.
* This is URL of classpath folder, zip or jar file.
* If code source is unknown, returns null (for example, for classes java.io.*).
*
* @param clazz For example, java.sql.SQLException.class
* @return for example, "file:/C:/jdev10/jdev/mywork/classes/"
* or "file:/C:/works/projects/classes12.zip"
*/
public static String getCodeSource(Class clazz)
{
if (clazz == null ||
clazz.getProtectionDomain() == null ||
clazz.getProtectionDomain().getCodeSource() == null ||
clazz.getProtectionDomain().getCodeSource().getLocation() == null)

// This typically happens for system classloader
// (java.lang.* etc. classes)
return null;

return clazz.getProtectionDomain()
.getCodeSource().getLocation().toString();
}

2/09/2008

Extending Maven SCM: proxy-scm

Problem description

Maven is a great tool, but it has one constraint that makes it inapplicable in many cases.
It can checkout the whole project (pom.xml and sources) from a single place in SCM storage using a single tag.

It's quite suitable in open-source projects, where new versions of projects don't appear too often.
But in our company we have a lot of components, each of which is stored in its own place in SCM and tagged with its own tag. To make using Maven possible, we need to create either BASH or ANT script which would check out all required projects from SCM.

This is the concern:

why do I need Maven if i still need ANT for running it?

If only Maven could do it by itself...

After thinking a while, we have created a solution.

Solution

As known, Maven SCM plugin has several SCM providers: CVS, Subversion, ClearCase etc.
Idea is to create one more provider, Proxy SCM. Its main goal is to checkout different modules from different places calling other SCM providers.
We need to extend pom.xml with 2 attributes:
  • /project/modules/module@scmProvider
  • /project/modules/module@scmHost
  • /project/modules/module@path
  • /project/modules/module@tag


Given pom.xml of form:

<project>
<scm>
<connection>scm:proxyscm:cvs:pserver:@myhost:dir:nodule</connection>
<developerConnection>scm:proxyscm:cvs:pserver:bla-bla</developerConnection>
</scm>
<modules>
<module path="robots/terminator/sdk">terminator_sdk</module>
<module path="robots/terminator/ui" tag="terminator_ui_v1-2">terminator_ui</module>
<module scmProvider="svn" scmHost="http://svn.apache.org/repos/asf/commons/proper/launcher" path="trunk">commons_launcher</module>
</modules>
</project>
,

The proxy-scm provider checks out all the 3 projects into 3 subfolders in current folder, resulting in the following content:
  • pom.xml
  • terminator_sdk
  • pom.xml
  • src
  • terminator_ui
  • pom.xml
  • src
  • commons_launcher
  • pom.xml
  • src


Implementation

The code of proxy-scm provider is quite simple: it just needs to parse file "pom.xml" and call appropriate SCM provider for every module.

I would like to commit this code into the next Maven SCM version (1.1). But it seems those guys don't respond to JRA issues neither commit patches into new version :(

2/08/2008

Null-Initialization in Java

In many languages (C++), uninitialized variables lead to errors. So, it's considered to be a good style to initialize every variable with 0 or null.

however, in Java it's quite the opposite.
Initialization with null is not recommended for those class members that should not be null.

Instead, it's recommended to declare those members final and initialize only once with an adequate value.


See example below.


Bad practice:

private CCollectorTarget m_collectTrg = null;

private CCollectorSchema m_collectSchm = null;

private CCollectorProject m_collectPrj = null;

private CCollectorFiles m_collectFiles = null;

public CCollectorData( CCollectorTarget collectTrg,
CCollectorSchema collectSchm,

CCollectorProject collectPrj,

CCollectorFiles collectFiles )

{

m_collectSchm = collectSchm;
m_collectTrg = collectTrg;
m_collectPrj = collectPrj;
m_collectFiles = collectFiles;
}

Good practice:

private final CCollectorTarget m_collectTrg;
private final CCollectorSchema m_collectSchm;
private final CCollectorProject m_collectPrj;
private final CCollectorFiles m_collectFiles;

public CCollectorData( CCollectorTarget collectTrg,
CCollectorSchema collectSchm,

CCollectorProject collectPrj,

CCollectorFiles collectFiles )

{

m_collectSchm = collectSchm;
m_collectTrg = collectTrg;
m_collectPrj = collectPrj;
m_collectFiles = collectFiles;
}

This technique has additional advantages: java compiler detects cases when you

  1. forget to initialize variable, or
  2. try to initialize it more than once.