Scott is a Senior Software Architect at Altamira Corporation. He has been developing enterprise and web applications for over 15 years professionally, and has developed applications using Java, Ruby/Rails, Groovy/Grails and Python. His main areas of interest include object-oriented design, system architecture, testing, and frameworks of all types including Spring, Hibernate, Ruby on Rails, Grails, and Django. In addition, Scott enjoys learning new languages to make himself a better and more well-rounded developer a la The Pragmatic Programmers' advice to "learn one language per year." Scott is a DZone MVB and is not an employee of DZone and has posted 43 posts at DZone. You can read more from them at their website. View Full User Profile

Can Java Be Saved?

11.10.2009
| 17860 views |
  • submit to reddit

The Java language has been around for a pretty long time, and in my view is now a stagnant language. I don't consider it dead because I believe it will be around for probably decades if not longer. But it appears to have reached its evolutionary peak, and it doesn't look it's going to be evolved any further. This is not due to problems inherent in the language itself. Instead it seems the problem lies with Java's stewards (Sun and the JCP) and their unwillingness to evolve the language to keep it current and modern, and more importantly the goal to keep backward compatibility at all costs. Not just Sun, but also it seems the large corporations with correspondingly large investments in Java like IBM and Oracle aren't exactly chomping at the bit to improve Java. I don't even know if they think it even needs improvement at all. So really, the ultra-conservative attitude towards change and evolution is the problem with Java from my admittedly limited view of things.

That's why I don't hate Java. But, I do hate the way it has been treated by the people charged with improving it. It is clear many in the Java community want things like closures and a native property syntax but instead we got Project Coin. This, to me, is sad really. It is a shame that things like closures and native properties were not addressed in Java/JDK/whatever-it-is-called 7.

Why Not?

I want to know why Java can't be improved. We have concrete examples that it is possible to change a major language in major ways. Even in ways that break backward compatibility in order to evolve and improve. Out with the old, in with the new. Microsoft with C# showed that you can successfully evolve a language over time in major ways. For example C# has always had a property syntax but it now also has many features found in dynamically typed and functional languages such as type inference and, effectively, closures. With LINQ it introduced functional concepts. When C# added generics they did it correctly and retained the type information in the compiled IL, whereas Java used type-erasure and simply dropped the types from the compiled bytecode. There is a great irony here: though C# began life about five or six years after Java, it not only has caught up but has surpassed Java in most if not all ways, and has continued to evolve while Java has become stagnant.

C# is not the only example. Python 3 is a major overhaul of the Python language, and it introduced breaking changes that are not backwards compatible. I believe they provide a migration tool to assist you should you want to move from the 2.x series to version 3 and beyond. Microsoft has done this kind of thing as well. I remember when they made Visual Basic conform to the .NET platform and introduced some rather gut wrenching (for VB developers anyway) changes, and they also provided a tool to aid the transition. One more recent example is Objective-C which has experienced a resurgence in importance mainly because of the iPhone. Objective-C has been around longer than all of Java, C#, Ruby, Python, etc. since the 1980s. Apple has made improvements to Objective-C and it now sports a way to define and synthesize properties and most recently added blocks (effectively closures). If a language that pre-dates Java (Python also pre-dates Java by the way) can evolve, I just don't get why Java can't.

While it is certainly possible to remain on older versions of software, forcing yourself to upgrade can be a Good Thing, because it ensures you don't get the "COBOL Syndrome" where you end up with nothing but binaries that have to run on a specific hardware platform forever and you are trapped until you rewrite or you go out of business. The other side of this, of course, is that organizations don't have infinite time, money, and resources to update every single application. Sometimes this too can be good, because it forces you to triage older systems, and possibly consolidate or outright eliminate them if they have outlived their usefulness. In order to facilitate large transitions, I believe it is very important to use tools that help automate the upgrade process, e.g. tools that analyze code and fix it if possible (reporting all changes in a log) and which provide warnings and guidance when a simple fix isn't possible.

The JVM Platform

Before I get into the changes I'd make to Java to make it not feel like I'm developing with a straightjacket on while having to type masses of unnecessary boilerplate code, I want to say that I think the JVM is a great place to be. Obviously the JVM itself facilitates developing all kinds of languages as evidenced by the huge number of languages that run on the JVM. The most popular ones and most interesting ones these days are probably JRuby, Scala, Groovy, and Clojure though there are probably hundreds more. So I suppose you could make an argument that Java doesn't need to evolve any more because we can simply use a more modern language that runs on the JVM.

The main problem I have with that argument is simply that there is already a ton of Java code out there, and there are many organizations who are simply not going to allow other JVM-based languages; they're going to stick with Java for the long haul, right or wrong. This means there is a good chance that even if you can manage convince someone to try writing that shiny new web app using Scala and its Lift framework, JRuby on Rails, Grails, or Clojure, chances are at some point you'll also need to maintain or enhance existing large Java codebases. Wouldn't you like to be able to first upgrade to a version of Java that has closures, native property syntax, method/property handles, etc?

Next I'll choose what would be my top three choices to make Java much better immediately.

Top Three Java Improvements

If given the chance to change just three things about Java to make it better, I would choose these:

  • Remove checked exceptions
  • Add closures
  • Add formal property support

I think these three changes along would make coding in Java much, much better. Let's see how.

Remove Checked Exceptions

By removing checked exceptions you eliminate a ton of boilerplate try/catch clauses that do nothing except log a message, wrap and re-throw as a RuntimeException, pollute the API with throws clauses all over the place, or worst of all empty catch blocks that can cause very subtle and evil bugs. With unchecked exceptions, developers still have the option to catch exceptions that they can actually handle. It would be interesting to see how many times in a typical Java codebase people actually handle exceptions and do something at the point of exception, or whether they simply punt it away for the caller to handle, who in turn also punts, and so forth all the way up the call stack until some global handler catches it or the program crashes. If I were a betting man, I'd bet a lot of money that for most applications, developers punt the vast majority of the time. So why force people to handle something they cannot possible handle?

Add Closures

I specifically listed removing checked exceptions first, because to me it is the first step to being able to have a closure/block syntax that isn't totally horrendous. If you remove checked exceptions, then adding closures would seem to be much easier since you don't need to worry at all about what exceptions could possibly be thrown and there is obviously no need to declare exceptions. Closures/blocks would lead to better ability to handle collections, for example as in Groovy but in Java you would still have types (note I'm also using a literal property syntax here):

// Find all people whose last name is "Smith"
List<Person> peeps = people.findAll { Person person -> person.lastName.equals("Smith");   } 

or
// Create a list of names by projecting the name property of a bunch of Person objects
List<String> names = people.collect { Person person -> person.name; }

Not quite as clean as Groovy but still much better than the for loops that would traditionally be required (or trying to shoehorn functional-style into Java using the Jakarta Commons Collections or Google Collections). Removal of checked exceptions would allow, as mentioned earlier, the block syntax to not have to deal with declaring exceptions all over the place. Having to declare checked exceptions in blocks makes the syntax worse instead of better, at least when I saw the various closure proposals for Java/JDK/whatever 7 which did not get included. Requiring types in the blocks is still annoying, especially once you get used to Ruby and Groovy, but it would be passable.

Native Property Syntax

The third change should do essentially what Groovy for properties does but should introduce a "property" keyword (i.e. don't rely on whether someone accidentally put an access modifier in there as Groovy does). The syntax could be very clean:

property String firstName;
property String lastName;
property Date dateOfBirth;

The compiler could automatically generate the appropriate getter/setter for you like Groovy does. This obviates the need to manually code the getter/setter. Like Groovy you should be able to override either or both. It de-clutters code enormously and removes a ton of lines of silly getter/setter code (plus JavaDocs if you are actually still writing them for every get/set method). Then you could reference properties as you would expect: person.name is the "getter" and person.name = "Fred" is the "setter." Much cleaner syntax, way less boilerplate code. By the way, if someone used the word "property" in their code, i.e. as a variable name, it is just not that difficult to rename refactor, especially with all the advanced IDEs in the Java community that do this kind of thing in their sleep.

Lots of other things could certainly be done, but if just these three were done I think Java would be much better off, and maybe it would even come into the 21st century like Objective-C. (See the very long but very good Ars Technica Snow Leopard review for information on Objective-C's new blocks feature.)

Dessert Improvements

If (as I suspect they certainly will :-) ) Sun/Oracle/whoever takes my suggestions and makes these changes and improves Java, then I'm sure they'll want to add in a few more for dessert. After the main course which removes checked exceptions, adds closures, and adds native property support, dessert includes the following:

  • Remove type-erasure and clean up generics
  • Add property/method handles
  • String interpolation
  • Type inference
  • Remove "new" keyword
Clean Up Generics

Generics should simply not remove type information when compiled. If you're going to have generics in the first place, do it correctly and stop worrying about backward compatibility. Keep type information in the bytecode, allow reflection on it, and allow me to instantiate a "new T()" where T is some type passed into a factory method, for example. I think an improved generics implementation could basically copy the way C# does it and be done.

Property/Method Handles

Property/method handles would allow you to reference a property or method directly. They would make code that now must use strings strongly typed and refactoring-safe (IDEs like IntelliJ already know how to search in text and strings but can never be perfect) much nicer. For example, a particular pet peeve of mine and I'm sure a lot of other developers is writing Criteria queries in Hibernate. You are forced to reference properties as simple strings. If the lastName property is changed to surname then you better make sure to catch all the places the String "lastName" is referenced. So you could replace code like this:

session.createCriteria(Person.class)
        .add(Restrictions.eq("lastName", "Smith")
        .addOrder(Order.asc("firstName")
        .list();

with this using method/property handles:

session.createCriteria(Person.class)
        .add(Restrictions.eq(Person.lastName, "Smith")
        .addOrder(Order.asc(Person.firstName)
        .list();

Now the code is strongly-typed and refactoring-safe. JPA 2.0 tries mightily to overcome having strings in the new criteria query API with its metamodel. But I find it pretty much appalling to even look at, what with having to create or code-generate a separate "metamodel" class which you reference like "_Person.lastName" or some similar awful way. This metamodel class lives only to represent properties on your real model object for the sole purpose of making JPA 2.0 criteria queries strongly typed. It just isn't worth it and is total overkill. In fact, it reminds me of the bad-old days of rampant over-engineering in Java (which apparently is still alive and well in many circles but I try to avoid it as best I can). The right thing is to fix the language, not to invent something that adds yet more boilerplate and more complexity to an already overcomplicated ecosystem.

Method handles could also be used to make calling methods using reflection much cleaner than it currently is, among other things. Similarly it would make accessing properties via reflection easier and cleaner. And with only unchecked exceptions you would not need to catch the four or five kinds of exceptions reflective code can throw.

String Interpolation

String interpolation is like the sorbet that you get at fancy restaurants to cleanse your palate. This would seem to be a no-brainer to add. You could make code like:

log.error("The object of type  ["
    + foo.getClass().getName()
    + "] and identifier ["
    + foo.getId()
    + "] does not exist.", cause);

turn into this much more palatable version (using the native property syntax I mentioned earlier):

log.error("The object of type [${foo.class.name}] and identifier [${foo.id}] does not exist.", cause);

Type Inference

I'd also suggest adding type inference, if only for local variables like C# does. Why do we have to repeat ourselves? Instead of writing:

Person person = new Person();

why can't we just write:

var person = new Person();

I have to believe the compiler and all the tools are smart enough to infer the type from the "new Person()". Especially since other strongly-typed JVM languages like Scala do exactly this kind of thing.

Elminate "new"

Last but not least, and actually not the last thing I can think of but definitely the last I'm writing about here, let's get rid of the "new" keyword and either go with Ruby's new method or Python's constructor syntax, like so:

// Ruby-like new method
var person = Person.new()

// or Python-like construction
var person = Person()

This one came to me recently after hearing Bruce Eckel give an excellent talk on language evolution and archaeology. He had a ton of really interesting examples of why things are they way they are, and how Java and other languages like C++ evolved from C. One example was the reason for "new" in Java. In C++ you can allocate objects on the stack or the heap, so there is a stack-based constructor syntax that does not use "new" while the heap-based constructor syntax uses the "new" operator. Even though Java only has heap-based object allocation, it retained the "new" keyword which is not only boilerplate code but also makes the entire process of object construction pretty much immutable: you cannot change anything about it nor can you easily add hooks into the object creation process.

I am not an expert at all in the low-level details, and Bruce obviously knows what he is talking about way more than I do, but I can say that I believe the Ruby and Python syntaxes are not only nicer but more internally consistent, especially in the Ruby case because there is no special magic or sauce going on. In Ruby, new is just a method, on a class, just like everything else.

Conclusion to this Way Too Long Blog Entry

I did not actually set out to write a blog whose length is worthy of a Ted Neward blog. It just turned out that way. (And I do in fact like reading Ted's long blogs!) Plus, I found out that speculative fiction can be pretty fun to write, since I don't think pretty much any of these things are going to make it into Java anytime soon, if ever, and I'm sure there are lots of people in the Java world who hate things like Ruby won't agree anyway.

From http://www.nearinfinity.com/blogs/scott_leberknight

Published at DZone with permission of Scott Leberknight, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Liam Knox replied on Tue, 2009/11/10 - 6:58pm in response to: Thomas Mueller

I hate this sentiment of using Scala is the solution for Java not having Closures. It is not. Given the massive investment in Java by large firms they will not just start using Scala blindly or in any great take up in the near future. Scala is not the effective solution to Java's lack of Closures. It is a totally moot point.

Should Java have closures?, yes I feel so but obviously it is contencious still. Is Java dead?, no not by along shot, but its evolution is obviously a little slower than many would like. I do feel it is evolving in a controlled, sensible manner especially with regards to backward compatability.

I also agree with many of the posts, its a fantastic language overall, and just look what has been built with it. Given its usage now I would much rather control in the evolution rather than some free for all.

Joakim Ohlrogge replied on Wed, 2009/11/11 - 2:04am

I couldn't agree more about the checked exceptions.
I couldn't agree less on the proposed property syntax (advocates mutability), in fact, properties would be pretty far down on my list. I'd much rather have named constructor arguments + default constructor arguments like Scala 2.8 does. One thing that scala does that removes a lot of code is to declare constructor arguments as field in the class.
Closures would be useful in any programming language but there are a lot of things in Java that would make them feel pretty odd. For one thing, nothing returns a value in java. Would closures return the last value in the closure or would we use return? Would return exit the whole closure? If not, will if's return? What do we do about void?
I disagree that moving to Scala is such a big deal. Scala coexists nicely with java. You can still use java as is for legacy. Strangle the modules you want to convert to scala and do your new development in scala. I don't see that java as a language has a future other than the next cobol. It had a nice 13 or so years and it will exist for much longer but too many things in the language are wrong from a modern language perspective. The sooner we realize this and move to something with a future such as scala, the better.
Java is dead, long live the JVM!

Gergely Tóth replied on Wed, 2009/11/11 - 2:44am

Hi,

 

At the beginning I really hated this blog entry but after I read through I think you raised some really interesting suggestions that move things forward (for me at least it does). Thanks!

Stefan Langer replied on Wed, 2009/11/11 - 3:38am in response to: Loren Kratzke

And in what way can a RuntimeException not be handled by a transaction? All you have to do is use a try catch block and voila you have handled all Exceptions you ever wanted.

Lets pick up the example with the FileNotFoundException. You are saying it is perfectly allright to use a CheckedException since the Compiler enforces you to handle it but in most use cases the non existence of a file is a fatal error since there is something wrong with the expectations you have for the program. It would be better to check that the file exists before expecting it to exist and then provide a sane alternative in case it doesn't exist or fail.

The problem with checked exceptions is not the exception per se but that a checked exception becomes part of the API. And if there is one thing I learned from experience then it is that you will never know in what way your API is being used in the future. So saying that somebody somewhere MUST handle your exception is a very broad assumption to make on all code that ever uses your API. Now in contrast to that a RuntimeException will simply propagate up the call stack and anybody who WANTS to handle it MAY handle it or ignore it. If nobody handles it it is finally handled by the runtime and the process of execution is halted. No guessing at what to do without knowing exactly what cause the exception or do you know where each exception from further up the call stack actually originated? Just one clean solution namly killing execution and making who ever is responsible start over.
Now the problem arises that in most Java programs there is no concept of a failing process so there is no backup that can replace the first one. There are frameworks that actually handle this quite well. E.g. EJBs if an exception is encountered by the container the bean is thrown out and not used anymore and the exception is logged to inform others.
Handling Exception by making assumptions about the cause of the error can be a dangerouse thing and a CheckedException is actually a definite way of making somebody handle the Exception in a possible bad way. (This is not a general rule but one that I encountered frequently in code)
Another problem with CheckedExceptions is that they tend to occlude existing code bases and since they are part of the API they cannot be removed from the API without breaking backwards compatibility. Even in cases where the clientcode is not actually handling the exception but simply rethrowing it you will have to change the client code. So you are producing potential upgrade problems for your users.
A RuntimeException can be handled just as can be any CheckedException it can even be declared on the method that is throwing it using throws which clearly documents it without leaking any information to classes that are further up the callstack that take no interest whatsoever in the exception.

And as a last point please show me a code base that is more robust because of using CheckedException instead of RuntimeExceptions.

Stefan Langer replied on Wed, 2009/11/11 - 3:54am

Btw my comment on the blog is:

Let Java be and let it die or prosper the way it is now. If you are interested in advanced concepts and better abstractions then go use one of the newer languages like Groovy, JRuby and Scala, ... if not then code the way you always have.
I strongly believe you are missing out if you stick to Java all the time and you are limiting yourself in ways that inhibit your ability to produce better code.

Choose the tool best suited for the job and stop spreading fud or bashing stuff you do not know or have no experience with (this is intended as a pun so do not take it personally, but if you do then you have a bigger problem then Java having or not having closures)

And one more thing I do not buy for one minute that more expressive code is more clear to read. You can always produce unreadable code in any language and each language takes a certain amount of experience until reading the source code becomes second nature but once it is then precise and concise definitions are easier to read.

E.g.

List myResult = new ArrayList();
foreach(SomeClass myClass : someObjects) {
    if(myClass.isTrue())
        myResult.add(myClass);
}
//do something  with the filtered result 

and now in Scala

val result = someObjects.filter( _.isTrue() ) 
// do something with result

with for comprehensions

val result = for( el <- someObjects if el.isTrue())
    yield(el)

//do something with result

or in ruby

result = someObjects {|el| el.isTrue }
//do something with result

I personally favour the closure or block code over the standard java way and find it easier to read and because these constructs are part of the language you do not have to use libraries to get them. But then again this is me and I will surly not convince you if you have never actually used closures or blocks in code. This is actually kind of like trying to explain to a blind person what colors are. (Not ment as an offense)

Armin Ehrenreich replied on Wed, 2009/11/11 - 5:20am in response to: Stefan Langer

Just because Average Joe is just too dumb to use it and is not willing to learn is not a good reason to reject something.

Yes it is the best reason to reject something beside backwards compatibility! Java is a language that is used by many people for everyday problems with a focus on maintainability of code by different persons. Many of them are not even computer scientists. Java is also a tool for many persons from physicists to molecular biologists. Of course such a language has to be different in the concepts it uses to a language that is exclusively used by a small group of computer scientists like Erlang. So in sharp contrast to oh so many utterances it is not a concern of Java to formulate code as concise as possible but instead as understandable as possible for many persons. Look in every beginners textbook for programming that this includes a verbose style of programming, expressive variable names, many comments and so on. Take a short look at the examples discussed at the mailing lists at the Scala site. It will take quite a while until the majority of developers can understand many of them if this day will come at all. And by the way, arrogance is also a bad fundament for working in a team.

Armin Ehrenreich replied on Wed, 2009/11/11 - 5:47am in response to: Stefan Langer

I fully agree to you are missing out if you stick to Java all the time. I also like using closures for example in Scala.

But the point is that Java and it APIs have been build without closures. That is not the case for Ruby. If you look at the APIs and the language (how it is used) there are profound differences. It is in my opinion a bad idea to try to change the basic concept of Java. This is the mess that the BGGA proposal would have created. Especially as inner classes are already present in Java. I think using CICE would have enabled many fundamental use cases of closures at very moderate complexity. All the advanced stuff is for languages build around functional concepts.

Java proved to be very useful as it is, and can be improved by small tweaks here and there (Project Coin) out of practical experience. But be careful, as a feature that is introduced, can never be removed again.

Scala, Erlang and the most recent C# versions still have to prove their usability in the real world. In my opinion it is a wise approach to watch how they are doing and what their pitfalls are in reality. In my experience the most "elegant" C# constructs are not used yet or are ignored by many developers.

Stefan Langer replied on Wed, 2009/11/11 - 6:39am in response to: Armin Ehrenreich

Ok what exactly is arrogant when I say that I expect a certain professionalism when working with people. You wouldn't bring you car to just any guy would you? You wouldn't get heart surgery from a noob just because he is inexpensive would you? Then please tell me why you would let the same noob code software your business depends on
So what I'm trying to say is that the noob has to gain the experience necessary to do his job properly and this includes learning stuff like closures and computer science stuff. What does this mean? It means that the beginners or average Joe should be taken by the hand and led the way in order to develope craftmansship which by no means has anything to do with a degree. If you are willing to learn you can grasp any of these concepts with ease. Just go out there and ask.
If you are not willing to learn these concepts then I do not think that you should be in this profession.
One counter example I can come up with is Rails. It is used by a lot of none computer scientists and average Joes who somehow manage to use blocks. So I guess they can't be that hard to understand.

It is funny you mention physicist and biologist using Java as I see a lot of those folks turning up in the Scala community to learn the language because they are sick of the way Java does certain things. I mean physicist or biologist do not seem to be the persons that are not capable of grasping something like closures or higher order functions...

And the notion of understandable code is one which isn't quite as easy to answer as writing lots of comments and writing lots of boilerplate code.
By the way I never said that code which is as concise as possible is easy to understand and I agree that there is a certain degree at which point conciseness undermines understandability but having to code out every iterator is not helping understandability in a code base nor is having to use anonymous inner classes or 3-5 helper classes in order to build useful abstractions over lists. And I don't even want to start talking about writing concurrent stuff in Java...
And giving the Scala list as a counter example is a bit awkward as the language is pretty new and the stuff that is discussed on there is from people who have high experience so the discussion tend to be on advanced topics and not on beginners stuff. Read Programming in Scala and you will discover that you do not need to understand all the high level features of the language and all the gory details in order to use the language. But if you want to you can. Nobody is keeping you from writing the same code you do in Java when using Scala although I wouldn't see the reason why you would do this once you get a grasp of the more advanced stuff.

Stefan Langer replied on Wed, 2009/11/11 - 6:37am in response to: Armin Ehrenreich

Ok so I think we agree on one point that it is probably fruitless to try to introduce language level changes at this point into what we know as Java. I think it is best to let other languages take over from here and let Java do what is is capable of performing and possibly die in dignity

About proof of Scala: Twitter is build on Scala which definetly shows that it is capable to deliver... but it will be a couple of years til it will get into the mainstream application market if it ever does or if it will be replaced by something different. But it is nice to know that there are options out there.

Erlang is actually used in high availability Telecommunication Switches and has therefor long proven that it is capable of bringing business value in industrial settings. With high availability we are talking about no downtime in a couple of years thanks to the upgrade capabilities built into the language itself. There is also Wing3D written in Erlang or CouchDB.

Can't comment on C# since I haven't looked into it too closely yet.

Bruno Sofiato replied on Wed, 2009/11/11 - 8:44pm

Why there's is this trend that every knew breakthrough must be done at language level ? Java already has a richful set of features that is UNDERUSED, like the annotation's metadata facility, which became just a gloryfied, high-coupling configuration mechanism.

Java needs a new wave of frameworks/libraries who makes better use of this facility.

The real stagnation isn't at the language level, but at the framework/library level.

Stefan Langer replied on Thu, 2009/11/12 - 3:29am in response to: Bruno Sofiato

Now I definetly disagree with this as I do not see a stagnation at the framework library level.
Frameworks and DesignPatterns are always developed when the things they provide cannot be expressed elegantly with the language itself.
And annotations are basically a different way of preprocessing either at compile or runtime which in my eyes has never been so great. Now I do think annotations have their place but when they become the syntax of the language then it is questionable whether the resulting code is still readable and maintainable.
Just my 2cts.

Bruno Sofiato replied on Thu, 2009/11/12 - 8:41am in response to: Stefan Langer

I fail to see any groundbreaking, innovative framework release lately, IMO, the framework library level is sadly stagnant. The java sucess reason's always have been it's rich set of open-source libraries.

Martin Wildam replied on Fri, 2009/11/13 - 9:36am

I didn't read the article - just the title caused increasing my blood pressure: NOT AGAIN that discussion!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.