My question is rather theoretical:
Lets say I have Java 8 project using static and default methods in interfaces. I need to get rid of them because I am porting the code to Java 7 (lets say it is an Android app prior to Android N).
How to do that? I know there is Retrolambda plugin for that and I successfully use it. But I am thinking about "pure" solution done by altering the code. What are the options?
I know I can remove static and default methods from interface and put them into abstract class that implements this interface and then alter the code wherever there is reference to that interface (this is what Retrolambda does, if I understood the process correctly).
My teacher had indicated it can be done just by "suitable change in interface definition" without using additional class. But so far I failed in finding the solution. I tried putting implementations directly into target classes but that caused a series of troubles in my particular project.
Anyone have some idea or clue I am missing?
I think it is at first questionable that you actually used such a thing in your Java8 project. Interface should remain without implementations. Default methods were introduced mainly for APIs backward compatibility(if method has to be added to Interface from previous versions and you can't afford to force users of API to change their code and you don't like creating InterfaceV2). Not for "daily usage".
I think when porting, you should just export the methods to the static *Util classes. This way you can reuse it and you are not breaking the main principle of Interface.
Recently we had to upgrade from itext version 5 to version 5.2 since the latter has support for Chinese languages. However one major change in the newer version is that it no longer supports the TextProvidingRenderListener class in the contsructor of PdfTextExtractor class. There was a class we had customized to utilize this feature :
public class CustomLocationAwarePdfRenderListener
implements TextProvidingRenderListener
{
public CustomLocationAwarePdfRenderListener( int lineAlignErrorAllowed)
{
this.lineAlignErrorAllowed = lineAlignErrorAllowed;
reset();
}
However as an outcome there is now no way to use this class and the attribute called lineAlignErrorAllowed which was part of the constructor of the CustomLocationAwarePdfRenderListener. The lineAlignErrorAllowed basically acts as a parameter of scanning the minimum no. of lines before the source is considered as too complex.
Any help on this topic would really be appreciated.
Managed to fix this issue after realizing that the LocationTextExtractionStrategy class in the latest itext version is a worthy replacement for the TextProvidingRenderListener. Although this time we had to extend the class. The only other change that was needed is to alter to code to use the now static PdfTextExtractor class by passing an instance of the LocationTextExtractionStrategy to the getTextFromPage. Had a bit of a struggle searching for the latest itext API reference doc, but got them here finally. (Somehow they show up with some different formatting than regular java API docs but one can live with that).
I generally oppose extension since it creates a very strong connection between classes, which is easy to accidentally break.
However, I finally thought I'd found a reasonable case for it - I want to optionally use a compressed version of a file type in an existing system. The compressed version would be almost as quick as the uncompressed, and would have exactly the same methods available (i.e. read and write) - the only difference would be in the representation on disk. Therefore, I had the compressed version extend the uncompressed version so that either kind of file could be used, just by optionally insantiating the other type.
public class CompressedSpecialFile extends SpecialFile(){ ... }
if (useCompression){
SpecialFile = new CompressedSpecialFile();
} else {
SpecialFile = new SpecialFile();
}
However, at a later point in the program, we use reflection:
Object[] values = new Object[]{SpecialFile sf, Integer param1, String param2, ...}
Class myclass = Class.forName(algorithmName);
Class[] classes = // created by calling .getClass on each object in values
constructor = myclass.getConstructor(classes);
Algorithm = (Algorithm) constructor.newInstance(values)
Which all worked fine, but now the myclass.getConstructor class throws a NoSuchMethodException since the run-time type of the SpecialFile is CompressedSpecialFile.
However, I thought that was how extension is supposed to work - since CompressedSpecialFile extends SpecialFile, any parameter accepting a SpecialFile should accept a CompressedSpecialFile. Is this an error in Java's reflection, or a failure of my understanding?
Hmm, the response to this bug report seems to indicate that this is intentional.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4301875
We cannot make this change for compatibility reaons. Furthermore, we
would expect that getConstructor should behave analogously to getDeclaredMethod,
which also requires an exact match, thus it does not make sense to change one
without changing the other. It would be possible to add an additional suite of
methods that differed only in the way in which the argument types were matched,
however.
There are certainly cases where we might want to apply at runtime during
reflection the same overload-resolution algorithm used statically by the
compiler, i.e., in a debugger. It is not difficult to implement this
functionality with the existing API, however, so the case for adding this
functionality to core reflection is weak.
That bug report was closed as a duplicate of the following one, which provides a bit more implementation detail:
http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=1b08c721077da9fffffffff1e9a6465911b4e?bug_id=4287725
Work Around
Users of getMethod must be precise identifying the Class passed to the argument.
Evaluation
The essence of this request is that the user would like for Class.getMethod
to apply the same overloading rules as the compiler does. I think this is
a reasonable request, as I see a need for this arising frequently in certain
kinds of reflective programs, such as debuggers and scripting interpreters,
and it would be helpful to have a standard implementation so that everybody
gets it right. For compatibility, however, the behavior of the existing
Class.getMethod should be left alone, and a new method defined. There is
a case for leaving this functionality out on the basis of footprint, as it
can be implemented using existing APIs, albeit somewhat inefficiently.
See also 4401287.
Consensus appears to be that we should provide overload resolution in
reflection. Exactly when such functionality is provided would depend largely
on interest and potential uses.
For compatibility reasons, the Class.get(Declared)+{Method,Constructor}
implementation should not change; new method should be introduced. The
specification for these methods does need to be modified to define "match". See
bug 4651775.
You can keep digging into those referenced bugs and the actual links I provided (where there's discussion as well as possible workarounds) but I think that gets at the reasoning (though why a new method reflecting java's oop in reflection as well has not yet been implemented, I don't know).
In terms of workarounds, I suppose that for the one-level-deep version of inheritance, you can just call getSuperclass() on each class whose name is that of the extending class, but that's extremely inelegant and tied to you using it only on your classes implementing in the prescribed manner. Very kludgy. I'll try and look for another option though.
Is there an "official" way to "unwrap" (i.e., obtain the non-enhanced class) for classes enhanced by Guice AOP?
So far, I detect these classes by looking for the string "$$EnhancerByGuice$$" in the class name and - if it is present - reverting to the superclass (Guice AOP works on classes using inheritance).
I'd prefer something that does not break when Guice decides to change this suffix string (which is by no means part of any API or contract).
As far as I can tell, there is no official way. There is an issue open to address it but given the prioritization I doubt it will happen. In the meantime, if you want to avoid breaking when Guice decide to change the suffix string, add a unit test that proves you can detect an enhanced class.
I was studying the legacy API's in the Java's Collection Framework and I learnt that classes such as Vector and HashTable have been superseded by ArrayList and HashMap.
However still they are NOT deprecated, and deemed as legacy when essentially, deprecation is applied to software features that are superseded and should be avoided, so, I am not sure when is a API deemed legacy and when it is deprecated.
From the official Sun glossary:
deprecation: Refers to a class, interface, constructor, method or field that is no longer recommended, and may cease to exist in a future version.
From the how-and-when to deprecate guide:
You may have heard the term, "self-deprecating humor," or humor that minimizes the speaker's importance. A deprecated class or method is like that. It is no longer important. It is so unimportant, in fact, that you should no longer use it, since it has been superseded and may cease to exist in the future.
The #Deprecated annotation went a step further and warn of danger:
A program element annotated #Deprecated is one that programmers are discouraged from using, typically because it is dangerous, or because a better alternative exists.
References
java.sun.com Glossary
Language guide/How and When to Deprecate APIs
Annotation Type Deprecated API
Note that the official glossary does not define what "legacy" means. In all likelihood, it may be a term that Josh Bloch used without an exact definition. The implication, though, is always that a legacy class should never be used in new code, and better replacement exists.
Perhaps an old code using legacy but non-deprecated class requires no action, since for now at least, they aren't in danger of ceasing to exist in future version.
In contrast, deprecation explicitly warns that they may cease to exist, so action should be taken to migrate to the replacement.
Quotes from Effective Java 2nd Edition
For comparison on how these terms are used in context, these are quotes from the book where the word "deprecated" appears:
Item 7: Avoid finalizers: The only methods that claim to guarantee finalization are System.runFinalizersOnExit and its evil twin Runtime.runFinalizersOnExit. These methods are fatally flawed and have been deprecated.
Item 66: Synchronize access to shared mutable data: The libraries provide the Thread.stop method, but this method was deprecated long ago because it's inherently unsafe -- its use can result in data corruption.
Item 70: Document thread safety: The System.runFinalizersOnExit method is thread-hostile and has been deprecated.
Item 73: Avoid thread groups: They allow you to apply certain Thread primitives to a bunch of threads at once. Several of these primitives have been deprecated, and the remainder are infrequently used. [...] thread groups are obsolete.
By contrast, these are the quotes where the word "legacy" appears:
Item 23: Don't use raw types in new code: They are provided for compatibility and interoperability with legacy code that predates the introduction of generics.
Item 25: Prefer lists to arrays: Erasure is what allows generic types to interoperate freely with legacy code that does not use generics.
Item 29: Consider typesafe heterogeneous containers: These wrappers are useful for tracking down who adds an incorrectly typed element to a collection in an application that mixes generic and legacy code.
Item 54: Use native methods judiciously: They provide access to libraries of legacy code, which could in turn provide access to legacy data. [...] It is also legitimate to use native methods to access legacy code. [...] If you must use native methods to access low-level resources or legacy libraries, use as little native code as possible and test it thoroughly.
Item 69: Prefer concurrency utilities to wait and notify: While you should always use the concurrency utilities in preference to wait and notify, you might have to maintain legacy code that uses wait and notify.
These quotes were not carefully selected: they're ALL instances where the word "deprecated" and "legacy" appear in the book. Bloch's message is clear here:
Deprecated methods, e.g. Thread.stop, are dangerous, and should never be used at all.
On the other hand, e.g. wait/notify can stay in legacy code, but should not be used in new code.
My own subjective opinion
My interpretation is that deprecating something is admitting that it is a mistake, and was never good to begin with. On the other hand, classifying that something is a legacy is admitting that it was good enough in the past, but it has served its purpose and is no longer good enough for the present and the future.
A common interpretation is that Deprecated means that it will be removed in the near future, and Legacy means that it will remain for backwards compatibility or other reasons.
Both mean that they shouldn't be used by new code.
In the case of the JDK even Deprecated code will remain, since backwards compatibility is very important for the Java JDK.
Deprecation often denotes that there is an intention to remove the functionality at some point in the future, whereas legacy just implies it shouldn't be used in new code if at all possible (though may even then be needed for interop reasons).
Deprecation means that it's bad and shouldn't be used - File.toURL() is a prime example, since it doesn't create correct URLs from files with spaces in the path. It's simply not doing what it should, but since existing code might be using workarounds which would break if the bug was fixed
Legacy just means that it's old and there are ways of doing something which are generally, but not necessarily, better. Vector is a good example - it is a List implementation, but it's still got some ugly crap from the days before the Collections API (i.e., List) was designed. It is also synchronized, which means that you have to pay the synchronization fee even when using it in a single-threaded scenario (except for in some situations where the VM is clever). ArrayList is better if you want an array-backed list implementation since it's unsynchronized, and Collections.synchronizedList is more flexible when you want a synchronized list since it's a wrapper which can be used with all list implementations (linked lists, lists from Arrays.asList(T...), etc). However, if you do happen to want a synchronized, array-backed list implementation, then Vector is fine.
My interpretation is that Legacy code simply has newer counterparts that do the job better. It will, however, continue to receive bug fixes and other support. Deprecated code, on the other hand, is unsupported and won't receive dedicated bug fixes.
The Deprecated annotation gives a formal definition of a deprecated API. I don't think that a formal definition for legacy classes exists. Both actually mean that the class shouldn't be used in new code.
I have a suggestion - legacy refers to code that was written in the past, deprecated refers to the advice not to use it anymore. You can still use deprecated api, but you can't write legacy code, cuz you're writing it right now.
Just IMHO