Jawa / Говнокод #12960 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JLabel;

public class Pause extends JFrame{

	private int MAX;
	private static String s;
	
	Pause(){
		this.setSize(300, 300);
		this.setVisible(true);
	}

	public void text(String s, final Thread t){
		try {
			t.wait();
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		JLabel l = new JLabel(s);
		l.addMouseListener(new MouseListener() {
			
			@Override
			public void mouseClicked(MouseEvent e) {
				// TODO Auto-generated method stub
				t.notify();
			}
			@Override
			public void mousePressed(MouseEvent e) {
				// TODO Auto-generated method stub
			}
			@Override
			public void mouseReleased(MouseEvent e) {
				// TODO Auto-generated method stub
			}
			@Override
			public void mouseEntered(MouseEvent e) {
				// TODO Auto-generated method stub
			}
			@Override
			public void mouseExited(MouseEvent e) {
				// TODO Auto-generated method stub
			}
		});
		this.add(l);
	}
	
	public int getMAX() {
		return MAX;
	}

	public void setMAX(int mAX) {
		MAX = mAX;
	}
	
	public String getS() {
		return s;
	}

	public void setS(String s) {
		this.s = s;
	}

	public void appendS(String s) {
		this.s += s;
	}


	public static void main(final String[] args){
		final Pause p = new Pause();
		final Thread t = new Thread(new Runnable() {
			
			public void run() {
				
				for (int i = 0; i < p.getMAX(); ++i){
					p.appendS(i + " ");
					if (i+2 <= p.getMAX()){
						p.text(s, t); //error
					}
				}
			}
		});
		t.start();
	}
}

Прислала подруга, изучающая потоки в Java, с просьбой подсказать, почему выдаётся ошибка компиляции при использовании переменной t внутри run.

Придётся объяснять, что в этом коде неправильно вообще всё, от первой до последней строки...

Запостил: someone someone, (Updated )

Комментарии (39) RSS

  • А кстати, ОП, почему?
    t жеж final, компилятор вполне же мог сообразить, что это просто синоним this.
    Ответить
    • Использование в теле анонимного класса считается использованием переменной до инициализации. Что логично - инициализация переменной t будет закончана только по завершении инструкции присваивания.
      Ответить
      • Ну это как бы объясняет почему не получилось (но это ж не неразрешимая проблема, имена методов он же находит в любом порядке). Мог бы и тут постараться немного.
        Ответить
        • Я подозреваю, что причина тут в том, что в общем случае нельзя гарантировать, что метод, в котором используется неинициализированная переменная, не будет вызван в момент инициализации (например, из конструктора).

          Например, представьте, что в данном примере метод run вызывается из конструктора класса Thread (хоть это и не так, но компилятор об этом не знает). Тогда будет произведено обращение к переменной t до завершения создания объекта и тем более до того, как объект будет ей присвоен.
          Ответить
          • Ну это если смотреть на присваивание как на операцию, которую нужно как-то делать (как в С++). В Яве в присваивании ничего не сделать - в этом примере это, по сути, просто лексическое связывание, и технически ничего не мешает скомпилировать такой код всегда правильно. Почему я особенно бы рассчитывал на final в таком случае - он мог бы как раз помочь отличить лексическое связывание (просто ярлык для того, что находится справа от "=") от чего-то, что где-то по ходу программы будет еще менятся.
            Ответить
            • Я только что объяснила, почему это в общем случае невозможно.

              Как должен вести себя компилятор?

              Вот такой пример не компилируется:

              public abstract class Test {
              	public Test() {
              		run();
              	}
              	
              	public abstract void run();
              
              	public static void main(final String[] args) {
              		final Test t = new Test() {
              			@Override
              			public void run() {
              				System.out.println(t);
              			}
              		};
              	}
              
              }


              Что бы вывелось, если бы он компилировался?
              Ответить
              • В общем случае - да, возможно, просто нужно уточнять семантику того, что подразумевается под final. То, что пример не компилируется - это следствие того, что компилятор его не может скомпилировать, а не того, что он в принципе не может быть скомпилирован.

                > что бы вывелось?
                Точно то же самое, что и при попытке распечатать this. Почему так? - потому что t - ето просто лексическое связывание, и this тождественно t.
                Но если бы мы говорили об операции присваивания (и ситуация в которой t и this Test'a различны имела бы смысл), то тогда, конечно, так нельзя делать.

                По крайней мере, мой аргумент такой, что новичек, пусть и не осознано, но нашел нетривиальный случай, где размыта семантика языка, и причины по которым мжно было бы сделать так, как автору хотелось, мне видятся вполне легитимными.
                Ответить
                • В общем случае - не взлетит.

                  Хорошо, в данном случае компилятор может "догадаться" и заменить t на this (кстати, почему бы тогда программисту сразу и не написать this?). Но в общем случае так поступить нельзя.

                  Например, в приведённом выше коде участвуют два объекта: Thread и Runnable, при этом в тело объекта Runnable передаётся переменная типа Thread. Тут уже this не отделаешься. В момент конструирования объекта Runnable объект Thread ещё не существует.
                  Ответить
                  • Нет, вы все время пытаетесь перейти на детали реализации.
                    Во-первых, проблема, которую вы описываете - решаемая.
                    Во-вторых, вы игнорируете неоднозначность конструкции a = b в Яве. Неоднозначность заключается в том, что иногда ее можно воспринять как разрушающее присваивание, а иногда, как выражение тождества. В первом случае вы бы были правы. Во втором - нет. final имеет отличительную особенность - переменным с таким модификаторм нельзя ничего позже присваивать, и поэтому, интуитивно, хочется верить, что они выражают тождество.
                    Как вы это тождество реализуете? - уже ваши проблемы, но принципиально можно показать, что это всегда реализуемо.

                    Это та же проблема, которая возникает при определении рекурсии:
                    void f () { f(); }

                    Ведь вы же не считаете эту конструкцию невозможной потому что внутри f f "еще не известно".
                    Ответить
                    • Как реализовать рекурсию, понятно.

                      В Java - invokevirtual/invokeinterface/invokestatic/invokespecial и пусть дальше JVM разбирается.

                      В нативном коде компилятор в момент генерации тела функции уже знает её адрес или по крайней мере смещение от текущей инструкции.

                      Как реализовать вышеупомянутую конструкцию - совершенно непонятно.

                      Вот ещё кусок кода, Фома ты неверующий:

                      public abstract class Inner implements Runnable {
                       public Inner() { run(); }
                      }
                      
                      public class Test {
                      	public Test(Inner i) {
                      		i.run();
                      	}
                      
                      	public static void main(final String[] args) {
                      		final Test t = new Test(new Inner() {
                      			@Override
                      			public void run() {
                      				System.out.println(t);
                      			}
                      		});
                      	}
                      
                      }


                      Что прикажете делать компилятору? Ведь объекты создаются в строгой последовательности: сначала Inner, потом t. В момент выполнения конструктора объекта Inner объект t ещё не существует и, соответственно, не может быть передан по ссылке. А ссылка-то уже нужна!
                      Ответить
                      • Еще раз, t тождественно this, если мы считаем, что final - это такой способ выразить тождество. Следовательно, везде, где можно подставить this, t имело бы точно тот же смысл.

                        Вот, смотрите:
                        (* Declares class `test' with two methods: *)
                        (* `test' and `printme' *)
                        class test =
                        object
                          method test = (Printf.printf "Who are you? - %s\n")
                          method printme = "I am t"
                        end;;
                        
                        (* lexically binds `t' to the newly created instance of `test' *)
                        let t = new test in t#test (t#printme);;

                        И работает ведь! 😉
                        Ответить
                        • Я ему про Фому, он мне про Ерёму.

                          В последнем моём примере t не тождественно this. Потому что внутри объекта Inner this - это объект Inner, а t - ещё не созданный объект Test.
                          Ответить
                          • Я же говорю ... в которой t и this Test'a.... И, опять же, вы говорите, что это не возможно потому что компилятор так не умеет - и тут я с вами согласен. Но когда вы гороворите, что так нельзя в принципе, то тут вы ошибаетесь, потому что в принципе так можно, тому я вам привел наглядный пример. другой язык, другой компилятор такое позволяет делать.
                            Ответить
                          • Даже, пожалуй, пример был не совсем удачным, вот лучше будет:
                            let test x =
                            object
                              method printme = "I'm test"
                            end in
                            Printf.printf "printme: %s\n" (test test)#printme;;
                            Ответить
                                • ОКамл, только я сейчас начал понимать, что код не эквивалентный... 🙂 А эквивалентный что-то не получается.

                                  Или имеется в виду "."? - тогда Баш, наверное.
                                  Ответить
  • >swing
    Но зачем? Он же уебищен. Как и почти весь гуй на яве, но хотя бы есть SWT.
    Ответить
    • Мне когда-то давно в универе на курсе Явы дали какую-то книжку с примерами + MSI установщиком JDK... Там почему-то все премеры были сделаны на Swing.
      Ответить
        • Да, уже было, клипсу уже несколько лет тогда было. Просто преподаватель был ничем не интересующийся старпер, который принимал лабы только в распечатках, и даже не запускал никогда.
          Ответить
          • Вот ты сам на свой вопрос и ответил. Нам тоже давали swing, хз почему, но факт. Может потому, что препод был макоебом, а там он не настолько уебищен?
            Ответить
          • Я не знаю почему, но это так. Выглядит, как говно из 2000-х, даже хуже нативного гуя винды родом из windows 95.
            Ответить
            • Используйте нормальный look and feel. Nimbus, например, или третьей стороны.

              У Swing и SWT разные области применения. Swing (или его современная замена - JavaFX) хорош для приложений, где важен одинаковый вид приложения на разных платформах. SWT - для нативного вида.
              Ответить
              • >одинаковый вид приложения на разных платформах.
                Одинаковое говно на всех платформах, по крайней мере, на винде точно. Может одинаково мако- или прыщеблядский? Я еще не видел нормально смотрящегося на винде кроссплатформенного гуя. Или менее хуевый (QT, SWT), или совсем хуевый (GTK, все оставшиеся варианты явагуя). Ах да, swing еще и тормозной.

                А что за look and feel и как его менять?
                Ответить
                • Swing тормозной? В таком случае SWT будет ещё тормознее. Хотя бы потому, что SWT использует "тяжёлые" нативные виджеты, а Swing - "лёгкие", которые рисует сам.

                  Look and feel - тема оформления Swing. Кстати, конкретно под Windows есть и нативная тема оформления Swing. Под Linux/GTK теоретически тоже есть, но выглядит убого.

                  Как менять - погуглите на тему Nimbus.
                  Ответить
                  • Это делается на стороне программиста или юзера?

                    SWT, может быть, тоже тормозной, но ему хоть можно простить, а тормозному кривому уебищу - как-то не хочется.
                    Ответить
      • > свинг - прекрасен
        В зависимости от того, кто его танцует
        Ответить
        • Приверженцы свинга называют себя свингерами. Для облегчения общения с единомышленниками и поиска новых партнёров свингеры объединяются в свинг-клубы, в которые, как правило, принимаются уже сложившиеся пары.
          Ответить
  • просто в жаве есть жесткое правило: никаких ссылок вперед. нельзя ссылаться на еще необъявленную переменную. нельзя объявить константу или переменную, ссылаясь на константу, объявленную позже.
    возможно, это связано с однопроходным анализатором.
    Ответить

Добавить комментарий

Я, guest, находясь в здравом уме и твердой памяти, торжественно заявляю:

    А не использовать ли нам bbcode?


    8