6 ms·
Yes c# generics are often a lot less painful, due to lack of type erasure, and support for variable number of type parameters etc. Having said that, more is po
by benjiweber 12y ago
Yes c# generics are often a lot less painful, due to lack of type erasure, and support for variable number of type parameters etc.
Having said that, more is possible with Java than many people realise. You can read statically available generic type params at runtime (field declarations, supertype tokens etc) . There are also workarounds for most of the erasure problems like methods that vary by generic type parameter only.
Java has some nice things itself too. Structural typing of single method interfaces/lambdas makes numerous things much easier. Default methods on interfaces let you do trait-like things nicer than with extension methods. Static imports reduce verbosity. More powerful enums etc.
c# approach to type inference seems to be to infer the left hand side of expressions with "var". Java infers types on the right hand side with the diamond operator, method type parameter inference, lambda to interface type inference. The Java approach makes code using fluent chained invocations less verbose, while the c# approach reduces the verbosity of assigning to lots of intermediate variables.
- usea 12y agoC# also infers method type parameters, and will implicitly convert a lambda to a matching interface. Fluent code in C# is also very clean. They're great features in both languages.
- benjiweber 12y agoYou're right, c# does to some extent. On balance I do agree with you, and prefer the set of c# features. Just to clarify - I was thinking of things like this class Example { public static void Main() { Predicate<String> foo = Foo<String>(s => s.Contains("foo")); } static Predicate<T> Foo<T>(Predicate<T> p) { return p; } } I can't replace Foo<String> with just Foo on the right hand side on the 3rd line. I could, however, replace the left hand side with just "var". (n.b. I am using mono, with langver=5. It's possible this is improved in a newer version) Java will infer the RHS class Example { public static void main(String... args) { Predicate<String> foo = foo(s -> s.contains("foo")); } static <T> Predicate<T> foo(Predicate<T> p) { return p; } } The structural typing of lambdas in c# I believe works with delegates? I at least don't know how I'd do the following in c# (I suspect I'd have to define an override of the implicit type conversion operator from a delegate type to a class, or define an extension method called hello on a suitable type) public class Example { interface MyPredicate<T> { boolean test(T t); default void hello() { System.out.println("Hello"); } } public static void main(String... args) { MyPredicate<String> foo = s -> s.contains("foo"); foo.hello(); } } The structural typing here is really powerful, e.g. for working around things like type erasure ;) http://benjiweber.co.uk/blog/2015/02/20/work-around-java-same-erasure-errors-with-lambdas/ http://benjiweber.co.uk/blog/2015/02/20/work-around-java-sam... or more consise builder pattern http://benjiweber.co.uk/blog/2014/11/02/builder-pattern-with-java-8-lambdas/ http://benjiweber.co.uk/blog/2014/11/02/builder-pattern-with...
- usea 12y agoAwesome examples, thank you. I apologize if you already know all of this stuff. You seem much more knowledgeable about this than me. As for type inference, C# will sometimes infer the RHS from the LHS, lambdas are one example. Also sometimes it can figure out the types from a lambda. Here is an example where the type information for inference comes from the literal int zero: public void Main() { var i = 0; var list = MakeThree(() => i++); //[0, 1, 2] } private IList<T> MakeThree<T>(Func<T> generator) { return new List<T>{ generator(), generator(), generator() }; } C# will infer method type parameters from arguments. In your example, there wasn't a type on the parameter to use as a source of inference. You could write it instead as (contrived example): Predicate<string> p = s => s.Contains("foo"); var foo = Foo(p); The type parameter on Foo is inferred, as well as the variable type on the LHS. The only time I write a type on the LHS is when I am assigning a lambda to a variable. And that is not super often. More realistically though, you would be passing a collection or something else that already has a type. For example in LINQ's IEnumerable<T> extension methods, the IEnumerable<T> is the source of the type information as the first parameter to the extension method, so everything is inferred from there. It's extremely rare for me to type a method type parameter. I cannot remember the last time I did. I do write the types for constructors often, though. new List<int>(), new Dictionary<string, Foo>() etc. I guess the tradeoff is that in java you can infer a lambda's type by writing it on the LHS, while in C# you are going to have to get that type from somewhere else. Either write it on the RHS, or it will be written on some other variable or method. Also I use Func and Action every day, but I basically never use other delegate types. Your example of structure typing of lambdas looks weird to me. It's the same feeling I get when I see a downcast. I mean no criticism; I suspect this is a Blub reaction in me. I am not used to looking at the left-hand side to figure out the type for the right-hand side. Lambdas will implicitly convert to a Func or Action. A reference to a method with a matching signature will also convert. As a silly example: Select takes a Func<T, U>, and will implicitly convert a method that accepts the same T as its only parameter. public ShortTons ConvertTons(LongTons t) { ... } public void Main() { var readings = GetReadings(); var exportWeights = readings.Select(x => x.Weight).Select(ConvertTons); } In this example, there are elided, inferred type parameters everywhere. The source of the type is in the missing GetReadings method, and the Weight property on the reading type. That's pretty typical. That builder pattern is pretty cool. In C# you have object initialization syntax, which is similar in being more readable as to which members are getting set. However, that's pretty much incompatible with immutability afaik. It only lets you set public properties with public setters. Normally you would make something immutable by making your setters private, or leaving them off entirely. Public fields (even readonly ones) are strongly discouraged in C#. Either way, that prohibits object initialization. Thanks for responding, I learned a lot about Java.
- hacknat 12y agoHaving to rely on reflection is bad though.
- eropple 12y agoWhy? I hear this a lot, but I never understood it. I do a lot of work with reflection to achieve things I can't otherwise do (around application extension, dynamic systems for games, etc.). I mean, attributes are reflection, and they're absolutely invaluable.
- hacknat 12y agoBecause it's not maintainable in a large code base. You write your code, and move on to something else, someone else comes a long and does a refactor, but they and/or the IDE misses the member names that were in strings. The program crashes on the next deploy because it built just fine. Reflection is like anything else it can be used properly and it can be abused. I've seen it abused far too many times. I realize there are times in Java when reflection is unavoidable (Android's animation libraries rely on it), but I consider it to be a feature of C# that, so far, I've never had to use reflection in C#.
- marvelous 12y agoA killer feature of the Ceylon programming language is its typesafe metamodel. Metamodel references are refactored like everything else in eclipse.
- benjiweber 12y agoAbsolutely. My point is just that the Java generics not being available at runtime is a common complaint, but it's only partially true. Most of the issues can actually be resolved even with type erasure. e.g. method overloading can be resolved statically.