Repercussions of changing single class in large project?

Discussion in 'Java' started by Andy Dingley, Jun 28, 2006.

  1. Andy Dingley

    Andy Dingley Guest

    I have a large project - several hundreds of classes. If a single
    ..java file is changed, then obviously the compiled class is changed
    too. However I'm not sure how many other classes would be affected by
    this and would also change when recompiled.

    By "will change", I'm thinking of a pragmatic meaning here, somewhere
    between "the bytecode will be different" and "the program will be in
    error if you mix the compiled classes by deploying too few class
    files". I'm not interested in changes that merely cause recompilation,
    only the end result.


    What changes to source would cause dependent classes to change? Is it
    as simple as "anything in a signature, nothing outside a signature" ?

    Does the way in which a class is imported affect that dependent class'
    sensitivity to changes ?

    Are these changes transitive? If A depends on B which depends on C,
    will changing C "significantly" also cause A to change ?


    I appreciate this is a potentially complex question. If there are any
    simple rules that can be applied, I'd love to hear them.

    Thanks
    Andy Dingley, Jun 28, 2006
    #1
    1. Advertising

  2. Andy Dingley

    Chris Uppal Guest

    wrote:

    > What changes to source would cause dependent classes to change?


    As far as I know, the contents of one classfile -- as produced by the
    compiler -- are independent of the source (or classfile) for any other class,
    with the following exceptions:

    Obviously the faked-up relationship between inner and outer classes is tighter,
    but that shouldn't be an issue here.

    Numeric, boolean, and String fields (including non-static fields) which are
    declared to be final, and initialised to a compile-time constant, are inlined
    whereever they are referred to in any [other] class. Hence, if you change the
    values, then you must recompile the classes which use them.

    There is only one circumstance where changes can propogate transitively --
    that's when one class uses another class's constants to define its own. E.g:
    public class A { public static int X = 1; }
    public class B { public static int Y = A.Y * 10; }
    public class C { public static int Z = B.X * 10; }
    In the above case, changing A.X means that classes B /and/ C have be
    recompiled.

    The Java compiler does a great deal of early binding -- it checks that methods
    and fields exist in other classes (and that they are accessible, etc) before it
    will let you use them. So, clearly, removing a member could cause another
    class to become uncompilable. (Come to that, so can removing a class ;-)
    Changing the class tree in a way that changes a class's total set of methods
    and fields (including inherited ones) counts as adding or removing for these
    purposes.

    A more subtle, but related, point is that if you add or remove overloaded
    methods, then the compiler might change it's mind about how to resolve an
    overloaded method call. E.g. if you have in V1 of your class:
    public class XYZ
    {
    public void aMethod(int i) { .... }
    }
    and a caller in another class:
    xyz.aMethod('a');
    then the compiler will statically resolve that call to invoke the (possibly
    overriden) aMethod(int). If you now add an overload to XYZ
    public class XYZ
    {
    public void aMethod(int i) { .... }
    public void aMethod(char c) { .... }
    }
    and recompile /without/ recompiling the caller, then it will still call
    aMethod(int). If you now recompile the caller, then the static part of method
    resolution will now see the new method as a better match for the parameter, so
    it will generate code to invoke aMethod(char) instead.

    ============

    Some examples of changes which do not propogate to other classfiles:

    Changes to the bodies of methods in one class do not result in changes to any
    other classfile. Adding or removing members (fields, methods, nested classes)
    does not result in changes to any other classfile unless it refers to those
    specific members. Re-ordering members does not result in changes to any other
    classfile even if it /does/ use those members.

    An example of where a fairly drastic change to one class doesn't cause changes
    to another class's compiled form. If we start with XYZ in its first form
    above, and compile
    xyz.aMethod(0);
    against it, then we'll get a certain compiled form for the caller. If we now
    refactor XYZ into
    class ABC
    {
    public void aMethod(int i) { .... }
    }
    public class XYZ
    extends ABC
    {
    }
    and recompile the caller, then we'll get exactly the same compiled form as
    before.

    -- chris
    Chris Uppal, Jun 28, 2006
    #2
    1. Advertising

  3. Andy Dingley

    ddimitrov Guest

    Chris Uppal wrote:
    > Numeric, boolean, and String fields (including non-static fields) which are
    > declared to be final, and initialised to a compile-time constant, are inlined
    > whereever they are referred to in any [other] class. Hence, if you change the
    > values, then you must recompile the classes which use them.


    Just to add that since Java 1.5, the classfile format supports
    java.lang.Class literals as well.

    This means that if you have the following code:

    interface ReflectionConfiguration {
    Class<FooService> fooImplementation = FooServiceImpl.class;
    }

    class Client {
    public static void main(String[] args) {
    FooService foo =
    ReflectionConfiguration.fooImplementation.newInstance();
    foo.doSomething();
    }
    }

    And then swap the fooImplementation to point to different class, the
    change will be picked by the Client class if you use Java 1.4, but
    won't be picked by 1.5 (unless you compile with -target 1.4).

    This example is stupid, but in practice it happens.
    ddimitrov, Jun 28, 2006
    #3
  4. Andy Dingley

    Chris Uppal Guest

    ddimitrov wrote:

    > interface ReflectionConfiguration {
    > Class<FooService> fooImplementation = FooServiceImpl.class;
    > }
    >
    > class Client {
    > public static void main(String[] args) {
    > FooService foo =
    > ReflectionConfiguration.fooImplementation.newInstance();
    > foo.doSomething();
    > }
    > }
    >
    > And then swap the fooImplementation to point to different class, the
    > change will be picked by the Client class if you use Java 1.4, but
    > won't be picked by 1.5 (unless you compile with -target 1.4).


    I'm sorry but I don't understand what you mean.

    I /thought/ you meant that class literals were treated like final compile-time
    constants an inlined at the point of reference, which is plausible -- but very
    nasty if true ;-) But (I'm happy to say) that doesn't seem to be the case.
    E.g:

    ========================
    public class Constants
    {
    static final int A = 22;
    static final Class C = String.class;
    }
    ========================
    public class Test
    {
    public static void
    main(String[] args)
    {
    System.out.println(Constants.A);
    System.out.println(Constants.C);
    }
    }
    ========================

    In the first call to println(), the constant A is replaced by its value in the
    generated bytecode:

    getstatic java/lang/System/out Ljava/io/PrintStream;
    bipush 22
    invokevirtual java/io/PrintStream/println (I)V

    but the reference to the "constant" C is not:

    getstatic java/lang/System/out Ljava/io/PrintStream;
    getstatic Constants/C Ljava/lang/Class;
    invokevirtual java/io/PrintStream/println (Ljava/lang/Object;)V

    So if Constants.java is recompiled with a different vaue for C, Test.java does
    not have to be recompiled to pick that up. (That's using javac from Sun's
    1.5.0_06-b05.)

    -- chris
    Chris Uppal, Jun 29, 2006
    #4
  5. Andy Dingley

    ddimitrov Guest

    Please ignore my previous post

    It seems that I was wrong about my assumptions for the class literals.

    The actual problem that I've seen is that a class does not get
    initilized on it's first reference under java 1.5

    This is illustrated by the following snippet:

    public class Test {
    public static void main(String[] args) {
    System.out.println("showing " + SomeClass.class);
    }
    }

    class SomeClass {
    static { System.out.print("loading and "); }
    }

    If we use JDK 5, the following commands produce the respective output:

    >> javac *.java
    >> java Test

    showing class SomeClass

    >> javac -source 1.4 *.java
    >> java Test

    loading and showing class SomeClass
    ddimitrov, Jun 29, 2006
    #5
  6. Andy Dingley

    Chris Uppal Guest

    Re: Please ignore my previous post

    ddimitrov wrote:

    > The actual problem that I've seen is that a class does not get
    > initilized on it's first reference under java 1.5


    Or even -- to judge from your example -- when its java.lang.Class object is
    used (its toString() is invoked).

    Personally, I find that distasteful...

    -- chris
    Chris Uppal, Jun 30, 2006
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Randall Parker
    Replies:
    1
    Views:
    590
    S. Justin Gengo
    Dec 12, 2005
  2. Chris
    Replies:
    1
    Views:
    13,610
    Oisin
    Mar 24, 2006
  3. E11
    Replies:
    1
    Views:
    4,720
    Thomas Weidenfeller
    Oct 12, 2005
  4. chris brat
    Replies:
    1
    Views:
    617
    chris brat
    May 10, 2006
  5. Eric Layman
    Replies:
    3
    Views:
    616
    Rad [Visual C# MVP]
    Apr 14, 2007
Loading...

Share This Page